feat(artifacts): capture Claude Docs docs and session documents, claim unowned rows
- A Claude Docs `batch` that creates a doc is captured like an `Artifact` publish, titled by the doc's name and linked by its own frame URL. - At Stop/SessionEnd, each pptx/docx/xlsx/pdf/html a workspace session created or changed is snapshotted into its owner's library (`auto`); an unchanged file is skipped, a changed one refreshes its snapshot. - Server mode: a page with no owner carries a suggested owner (the workspace or home its session ran in). The suggested user claims or declines it; an admin claims or assigns any. New tab Unowned / Is this yours? on the Artifacts page; all three actions audited.
This commit is contained in:
@@ -38,6 +38,7 @@ const STATUS = {
|
||||
EMARKETFETCH: 502,
|
||||
ENEEDLOGIN: 502,
|
||||
ETOOBIG: 413,
|
||||
EOWNED: 409,
|
||||
};
|
||||
|
||||
function sendError(res, err) {
|
||||
@@ -362,6 +363,34 @@ router.post(
|
||||
})
|
||||
);
|
||||
|
||||
// A row nobody owns: its suggested user says "mine" or "not mine"; an admin
|
||||
// takes it or gives it to someone (artifacts.js `claim`/`decline`/`assign`).
|
||||
router.post(
|
||||
"/:id/claim",
|
||||
sameOriginGuard,
|
||||
wrap((req, res) => {
|
||||
const item = artifacts.claim(req, req.params.id);
|
||||
changed(item);
|
||||
res.json({ item });
|
||||
})
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/:id/decline",
|
||||
sameOriginGuard,
|
||||
wrap((req, res) => res.json(artifacts.decline(req, req.params.id)))
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/:id/assign",
|
||||
sameOriginGuard,
|
||||
wrap((req, res) => {
|
||||
const item = artifacts.assign(req, req.params.id, req.body?.userId);
|
||||
changed(item);
|
||||
res.json({ item });
|
||||
})
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/:id/uninstall",
|
||||
sameOriginGuard,
|
||||
|
||||
+23
-2
@@ -1295,9 +1295,13 @@ const processEventTx = db.transaction((hookType, data, tool = "claude", peer = n
|
||||
};
|
||||
broadcast("new_event", event);
|
||||
// A claude.ai page the session published goes into its owner's Artifacts.
|
||||
if (hookType === "PostToolUse" && toolName === "Artifact" && tool !== "codex") {
|
||||
// A Claude Docs doc counts too (`isPageTool`): it is a claude.ai page the session made.
|
||||
if (hookType === "PostToolUse" && tool !== "codex") {
|
||||
try {
|
||||
const row = require("../lib/artifacts").captureFromHook(sessionId, data, peer);
|
||||
const artifacts = require("../lib/artifacts");
|
||||
const row = artifacts.isPageTool(toolName)
|
||||
? artifacts.captureFromHook(sessionId, data, peer)
|
||||
: null;
|
||||
if (row) {
|
||||
broadcast("artifact_changed", { id: row.id, ownerId: row.owner_id, shared: false });
|
||||
}
|
||||
@@ -1736,6 +1740,23 @@ router.post("/event", (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
// Documents the session made (slides, Word, Excel, PDF, HTML) go into its
|
||||
// workspace owner's Artifacts when it stops. Off the response path, fail-safe.
|
||||
if (["Stop", "SessionEnd"].includes(hook_type) && data.session_id) {
|
||||
require("../lib/artifacts")
|
||||
.captureOutputs(data.session_id)
|
||||
.then((rows) => {
|
||||
for (const row of rows) {
|
||||
broadcast("artifact_changed", {
|
||||
id: row.id,
|
||||
ownerId: row.owner_id,
|
||||
shared: row.shared === 1,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => console.warn("[artifacts] output capture failed:", err.message));
|
||||
}
|
||||
|
||||
// Ingest Workflow-tool run journals from disk. Inner agent() calls emit NO
|
||||
// hooks, so the journal (written at workflow completion) is the only source.
|
||||
// Run on the lifecycle hooks that bracket a workflow finishing, off the
|
||||
|
||||
Reference in New Issue
Block a user