@sema-agent/core 7.15.0 → 7.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +202 -0
- package/dist/agents/builtin-agents.d.ts +3 -3
- package/dist/agents/cross-session-envelope.d.ts +0 -1
- package/dist/agents/cross-session-envelope.js +0 -1
- package/dist/agents/peer-notices.d.ts +0 -9
- package/dist/agents/peer-notices.js +0 -63
- package/dist/agents/subagent.js +7 -15
- package/dist/brain/request-params.d.ts +2 -2
- package/dist/config/defaults.d.ts +0 -7
- package/dist/config/defaults.js +0 -2
- package/dist/core/artifact-host.d.ts +218 -0
- package/dist/core/artifact-host.js +92 -0
- package/dist/core/ask-origin.d.ts +29 -3
- package/dist/core/ask-origin.js +17 -9
- package/dist/core/ask-question.d.ts +25 -0
- package/dist/core/ask-question.js +4 -0
- package/dist/core/ask-unresolvable-notice.d.ts +30 -21
- package/dist/core/ask-unresolvable-notice.js +16 -8
- package/dist/core/auto-compaction.d.ts +10 -10
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/background-agent-store.d.ts +5 -5
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-execution-record.d.ts +0 -3
- package/dist/core/checkpoint-execution-record.js +0 -4
- package/dist/core/checkpoint-store.d.ts +71 -99
- package/dist/core/checkpoint-store.js +9 -16
- package/dist/core/context-edit.d.ts +36 -104
- package/dist/core/context-edit.js +37 -60
- package/dist/core/effective-path-target.d.ts +68 -8
- package/dist/core/effective-path-target.js +42 -30
- package/dist/core/engine-notice.d.ts +46 -32
- package/dist/core/env-budget.d.ts +31 -0
- package/dist/core/env-budget.js +39 -0
- package/dist/core/exec-output-tail.d.ts +5 -5
- package/dist/core/file-history-store.js +3 -6
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.js +3 -3
- package/dist/core/gate-lanes.js +9 -15
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +18 -17
- package/dist/core/lsp-session.d.ts +1 -1
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/lsp.js +5 -4
- package/dist/core/mcp.d.ts +5 -5
- package/dist/core/mcp.js +4 -48
- package/dist/core/memory-engine/delegation-settlement.d.ts +37 -12
- package/dist/core/memory-engine/delegation-settlement.js +128 -39
- package/dist/core/memory-engine/engine.d.ts +9 -8
- package/dist/core/memory-engine/engine.js +115 -120
- package/dist/core/memory-engine/file-backend.d.ts +17 -5
- package/dist/core/memory-engine/file-backend.js +78 -34
- package/dist/core/memory-engine/index.d.ts +2 -2
- package/dist/core/memory-engine/index.js +2 -2
- package/dist/core/memory-engine/layout.d.ts +152 -54
- package/dist/core/memory-engine/layout.js +441 -104
- package/dist/core/memory-engine/types.d.ts +8 -3
- package/dist/core/memory-engine/types.js +5 -0
- package/dist/core/memory-recall.d.ts +0 -8
- package/dist/core/memory.d.ts +1 -1
- package/dist/core/oracle-isolation.js +2 -2
- package/dist/core/parent-spec-seat.d.ts +66 -0
- package/dist/core/parent-spec-seat.js +47 -0
- package/dist/core/permission-rule-model.d.ts +11 -6
- package/dist/core/permission-rule-model.js +53 -37
- package/dist/core/permission-rules.js +8 -3
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +19 -0
- package/dist/core/retired-keys.js +15 -0
- package/dist/core/runner/abort-race.d.ts +3 -3
- package/dist/core/runner/active-skill-scope.d.ts +2 -2
- package/dist/core/runner/active-skill-scope.js +3 -3
- package/dist/core/runner/assemble-result.d.ts +42 -40
- package/dist/core/runner/assemble-result.js +5 -4
- package/dist/core/runner/checkpoint-scope.d.ts +4 -4
- package/dist/core/runner/clock-and-limits.d.ts +3 -3
- package/dist/core/runner/compaction-call-options.d.ts +35 -99
- package/dist/core/runner/compaction-knobs.d.ts +4 -4
- package/dist/core/runner/compaction-seams.d.ts +5 -5
- package/dist/core/runner/contracts.d.ts +232 -233
- package/dist/core/runner/decide-continuation.d.ts +6 -6
- package/dist/core/runner/denial-limit-arms.d.ts +8 -8
- package/dist/core/runner/derived-route-fallback.d.ts +2 -2
- package/dist/core/runner/gate-exit.d.ts +6 -6
- package/dist/core/runner/gate-exit.js +15 -1
- package/dist/core/runner/git-status-frame.d.ts +7 -7
- package/dist/core/runner/halt-attribution.d.ts +35 -0
- package/dist/core/runner/halt-attribution.js +16 -0
- package/dist/core/runner/inherited-ask-grants.d.ts +6 -6
- package/dist/core/runner/memory-capture-optout.d.ts +1 -1
- package/dist/core/runner/memory-consolidation-driver.d.ts +4 -4
- package/dist/core/runner/memory-consolidation.d.ts +6 -8
- package/dist/core/runner/memory-consolidation.js +0 -1
- package/dist/core/runner/park-commit.d.ts +6 -6
- package/dist/core/runner/permission-rule-lanes.d.ts +19 -19
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +4 -4
- package/dist/core/runner/prepare-artifact.d.ts +53 -0
- package/dist/core/runner/prepare-artifact.js +77 -0
- package/dist/core/runner/prepare-ask-lane.d.ts +3 -3
- package/dist/core/runner/prepare-boundary-parks.d.ts +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +9 -7
- package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
- package/dist/core/runner/prepare-config-doors.d.ts +24 -24
- package/dist/core/runner/prepare-config-doors.js +5 -2
- package/dist/core/runner/prepare-context-lane.d.ts +8 -8
- package/dist/core/runner/prepare-context-lane.js +2 -13
- package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
- package/dist/core/runner/prepare-file-history.d.ts +3 -3
- package/dist/core/runner/prepare-file-history.js +3 -52
- package/dist/core/runner/prepare-gate-stations.d.ts +5 -5
- package/dist/core/runner/prepare-hands-readface.d.ts +35 -38
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +9 -6
- package/dist/core/runner/prepare-inherited-gate.js +7 -8
- package/dist/core/runner/prepare-listings.d.ts +3 -3
- package/dist/core/runner/prepare-lsp.d.ts +3 -3
- package/dist/core/runner/prepare-memory-engine-session.d.ts +5 -5
- package/dist/core/runner/prepare-memory.d.ts +4 -6
- package/dist/core/runner/prepare-memory.js +10 -9
- package/dist/core/runner/prepare-offload-wrappers.d.ts +2 -2
- package/dist/core/runner/prepare-park-ask.d.ts +6 -6
- package/dist/core/runner/prepare-policy-chain.d.ts +7 -7
- package/dist/core/runner/prepare-policy-chain.js +15 -30
- package/dist/core/runner/prepare-prompt-assembly.js +1 -1
- package/dist/core/runner/prepare-prompt-inputs.d.ts +5 -5
- package/dist/core/runner/prepare-protocol-tools.d.ts +1 -1
- package/dist/core/runner/prepare-question-face.d.ts +4 -4
- package/dist/core/runner/prepare-run-refs.d.ts +11 -11
- package/dist/core/runner/prepare-safety-scan.d.ts +2 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +4 -4
- package/dist/core/runner/prepare-suspend-saga.js +0 -2
- package/dist/core/runner/prepare-task.d.ts +12 -12
- package/dist/core/runner/prepare-task.js +10 -9
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +2 -3
- package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -4
- package/dist/core/runner/prepare-wiring-manifest.js +9 -7
- package/dist/core/runner/prepare-workspace-restore.d.ts +1 -1
- package/dist/core/runner/remote-env-retry.d.ts +3 -3
- package/dist/core/runner/resume-admission.d.ts +1 -1
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-claim.d.ts +1 -1
- package/dist/core/runner/resume-internals-and-config.d.ts +1 -1
- package/dist/core/runner/resume-internals-and-config.js +4 -0
- package/dist/core/runner/resume-policy-outcome.d.ts +1 -1
- package/dist/core/runner/resume-preflight.d.ts +1 -1
- package/dist/core/runner/resume-review-outcome.d.ts +1 -1
- package/dist/core/runner/retired-runner-deps.d.ts +3 -0
- package/dist/core/runner/retired-runner-deps.js +7 -0
- package/dist/core/runner/run-brain-sinks.d.ts +1 -1
- package/dist/core/runner/run-clock-and-content.d.ts +4 -4
- package/dist/core/runner/run-compaction-machinery.d.ts +3 -3
- package/dist/core/runner/run-git-lane.d.ts +1 -1
- package/dist/core/runner/run-harness-handlers.d.ts +2 -2
- package/dist/core/runner/run-harness-handlers.js +2 -3
- package/dist/core/runner/run-identity-wiring.d.ts +4 -4
- package/dist/core/runner/run-leg.d.ts +3 -3
- package/dist/core/runner/run-notification-lane.d.ts +2 -2
- package/dist/core/runner/run-reasoning-seat.d.ts +2 -2
- package/dist/core/runner/run-settle-and-teardown.d.ts +5 -5
- package/dist/core/runner/run-settle-and-teardown.js +3 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-terminal-adoption.d.ts +5 -5
- package/dist/core/runner/runtask.d.ts +14 -14
- package/dist/core/runner/session-file-state-replay.d.ts +3 -3
- package/dist/core/runner/session-file-state-replay.js +3 -3
- package/dist/core/runner/session-rule-policy.d.ts +2 -2
- package/dist/core/runner/session-rule-policy.js +3 -4
- package/dist/core/runner/steer-admission.d.ts +2 -2
- package/dist/core/runner/stream-halt-verbs.d.ts +1 -1
- package/dist/core/runner/stream-halt-verbs.js +1 -1
- package/dist/core/runner/stream-reap.d.ts +1 -1
- package/dist/core/runner/stream-steer-verb.js +1 -4
- package/dist/core/runner/synthetic-tools.d.ts +8 -8
- package/dist/core/runner/teardown-bounded.d.ts +2 -2
- package/dist/core/runner/tool-defer-gate.d.ts +1 -1
- package/dist/core/runner/tool-disclosure.d.ts +33 -33
- package/dist/core/runner/tool-end-body.d.ts +3 -3
- package/dist/core/runner/tool-face-overlay.d.ts +2 -2
- package/dist/core/runner/tool-output-projection.d.ts +2 -2
- package/dist/core/runner/turn-attachments.d.ts +53 -53
- package/dist/core/runner/usage-accounting.d.ts +4 -4
- package/dist/core/runner/workspace-path.d.ts +1 -1
- package/dist/core/runner/workspace-path.js +3 -0
- package/dist/core/runner-deps.d.ts +30 -49
- package/dist/core/safe-notify.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +4 -4
- package/dist/core/session.d.ts +2 -2
- package/dist/core/skill-tool-specifier.d.ts +1 -1
- package/dist/core/skills-directory.d.ts +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/strategy-store.d.ts +8 -11
- package/dist/core/task-event.d.ts +20 -17
- package/dist/core/task-limits.d.ts +1 -1
- package/dist/core/task-notification.d.ts +5 -5
- package/dist/core/task-registry-agent.d.ts +5 -5
- package/dist/core/task-registry-monitor.d.ts +2 -2
- package/dist/core/task-registry-shared.d.ts +27 -27
- package/dist/core/task-registry-shared.js +3 -13
- package/dist/core/task-registry.d.ts +8 -8
- package/dist/core/task-result.d.ts +37 -37
- package/dist/core/task-spec.d.ts +5 -5
- package/dist/core/task-stream.d.ts +16 -11
- package/dist/core/tool-catalog-entries.d.ts +1 -0
- package/dist/core/tool-catalog-entries.js +5 -0
- package/dist/core/tool-conformance.d.ts +0 -2
- package/dist/core/tool-conformance.js +1 -3
- package/dist/core/tool-errors.d.ts +5 -0
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +51 -22
- package/dist/core/tool-policy.js +28 -5
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +11 -11
- package/dist/core/tool-spec.d.ts +34 -38
- package/dist/core/tools.d.ts +2 -2
- package/dist/core/trace.d.ts +12 -15
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +4 -3
- package/dist/core/workflow-journal-store.d.ts +11 -11
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
- package/dist/engine/execution-env/node-execution-env.js +4 -2
- package/dist/engine/harness/agent-harness.d.ts +47 -4
- package/dist/engine/harness/agent-harness.js +52 -9
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +15 -8
- package/dist/engine/loop/types.d.ts +5 -0
- package/dist/index.d.ts +21 -16
- package/dist/index.js +17 -12
- package/dist/internal/harness.d.ts +1 -0
- package/dist/orchestration/run-spec.d.ts +3 -0
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +12 -6
- package/dist/orchestration/run-workflow-tool.js +23 -5
- package/dist/orchestration/workflow-primitives.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +1 -4
- package/dist/orchestration/workflow-types.d.ts +34 -4
- package/dist/orchestration/workflow-types.js +2 -0
- package/dist/orchestration/workflow.d.ts +39 -7
- package/dist/orchestration/workflow.js +234 -113
- package/dist/prompt-assembly/assemble.d.ts +2 -2
- package/dist/prompt-assembly/assemble.js +9 -9
- package/dist/prompt-assembly/composer.d.ts +6 -7
- package/dist/prompt-assembly/composer.js +10 -10
- package/dist/prompt-assembly/epoch.js +1 -1
- package/dist/prompt-assembly/event-registry.d.ts +0 -21
- package/dist/prompt-assembly/packs/sema-default.js +30 -30
- package/dist/prompt-assembly/types.d.ts +14 -14
- package/dist/prompts/coordinator.d.ts +0 -13
- package/dist/prompts/coordinator.js +0 -3
- package/dist/stores/file/checkpoint-store.js +1 -5
- package/dist/stores/file/file-history-store.js +2 -2
- package/dist/stores/file/permission-rule-store.d.ts +4 -35
- package/dist/stores/file/permission-rule-store.js +4 -148
- package/dist/tools/artifact/artifact-text.d.ts +62 -0
- package/dist/tools/artifact/artifact-text.js +68 -0
- package/dist/tools/artifact/artifact-tool.d.ts +52 -0
- package/dist/tools/artifact/artifact-tool.js +351 -0
- package/dist/tools/artifact/local-stub.d.ts +26 -0
- package/dist/tools/artifact/local-stub.js +296 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +114 -48
- package/dist/tools/fs/bash-readonly-classifier.js +171 -60
- package/dist/tools/fs/encoding.d.ts +31 -40
- package/dist/tools/fs/fs-bash.d.ts +49 -45
- package/dist/tools/fs/fs-bash.js +19 -27
- package/dist/tools/fs/fs-pdf.d.ts +8 -8
- package/dist/tools/fs/fs-search-tools.d.ts +4 -4
- package/dist/tools/fs/fs-search-tools.js +5 -2
- package/dist/tools/fs/fs-shared.d.ts +146 -158
- package/dist/tools/fs/fs-shared.js +3 -11
- package/dist/tools/fs/fs-write.d.ts +8 -8
- package/dist/tools/fs/gh-rate-limit.d.ts +13 -14
- package/dist/tools/fs/index.d.ts +78 -78
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/notebook.d.ts +24 -25
- package/dist/tools/fs/pdf.d.ts +3 -3
- package/dist/tools/fs/read-deny.d.ts +22 -22
- package/dist/tools/fs/read-face.d.ts +21 -24
- package/dist/tools/fs/repo-map.d.ts +8 -8
- package/dist/tools/fs/safety.d.ts +233 -130
- package/dist/tools/fs/safety.js +131 -31
- package/dist/tools/fs/search.d.ts +63 -71
- package/dist/tools/fs/search.js +8 -6
- package/package.json +6 -2
- package/test/export-surface.snapshot.json +119 -39
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { ARTIFACT_FAILURE_RESULT_CODE, ARTIFACT_HOST_ERROR_CODE, ARTIFACT_LIST_SCOPES, ARTIFACT_PLANE_CONTENT_ORIGIN, ARTIFACT_PUBLISH_CONTEXTS, } from "../../core/artifact-host.js";
|
|
4
|
+
import { ARTIFACT_TOOL_NAME, toolFace } from "../../core/tool-catalog-entries.js";
|
|
5
|
+
import { errorResult } from "../../core/tools.js";
|
|
6
|
+
import { inlineUntrusted } from "../../core/untrusted-text.js";
|
|
7
|
+
import { actionDescription, artifactDescription, PARAM_TEXT, urlDescription } from "./artifact-text.js";
|
|
8
|
+
const MAX_FILES_PER_PUBLISH = 255;
|
|
9
|
+
const LIST_LIMIT_DEFAULT = 25;
|
|
10
|
+
const LIST_LIMIT_MAX = 100;
|
|
11
|
+
const BINARY_NOTE_MEDIA = new Set(["application/json", "application/javascript", "text/javascript", "image/svg+xml", "application/xml"]);
|
|
12
|
+
const MEDIA_BY_EXT = new Map(Object.entries({
|
|
13
|
+
html: "text/html", htm: "text/html", css: "text/css", js: "text/javascript", mjs: "text/javascript", json: "application/json",
|
|
14
|
+
md: "text/markdown", txt: "text/plain", csv: "text/csv", xml: "application/xml", svg: "image/svg+xml", png: "image/png",
|
|
15
|
+
jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", ico: "image/x-icon", woff: "font/woff",
|
|
16
|
+
woff2: "font/woff2", ttf: "font/ttf", wasm: "application/wasm", pdf: "application/pdf", mp3: "audio/mpeg", mp4: "video/mp4",
|
|
17
|
+
}));
|
|
18
|
+
export function mediaTypeOf(path) {
|
|
19
|
+
const dot = path.lastIndexOf(".");
|
|
20
|
+
return (dot >= 0 ? MEDIA_BY_EXT.get(path.slice(dot + 1).toLowerCase()) : undefined) ?? "application/octet-stream";
|
|
21
|
+
}
|
|
22
|
+
export function isTextMedia(mediaType) {
|
|
23
|
+
if (mediaType === undefined)
|
|
24
|
+
return false;
|
|
25
|
+
const essence = mediaType.split(";")[0].trim().toLowerCase();
|
|
26
|
+
return essence.startsWith("text/") || BINARY_NOTE_MEDIA.has(essence);
|
|
27
|
+
}
|
|
28
|
+
export function artifactAttemptId(runId, toolCallId, occurrence = 0) {
|
|
29
|
+
const h = createHash("sha256").update(runId).update("|").update(toolCallId).update("|").update(String(occurrence)).digest("hex");
|
|
30
|
+
return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
|
|
31
|
+
}
|
|
32
|
+
const basenameOf = (p) => {
|
|
33
|
+
const i = Math.max(p.lastIndexOf("/"), p.lastIndexOf("\\"));
|
|
34
|
+
return i >= 0 ? p.slice(i + 1) : p;
|
|
35
|
+
};
|
|
36
|
+
export function normalizePublishedPath(raw) {
|
|
37
|
+
if (raw.length === 0)
|
|
38
|
+
return { ok: false, reason: "is empty" };
|
|
39
|
+
if (raw.includes("\0"))
|
|
40
|
+
return { ok: false, reason: "contains a NUL byte" };
|
|
41
|
+
if (raw.includes("\\"))
|
|
42
|
+
return { ok: false, reason: "contains a backslash (use forward slashes)" };
|
|
43
|
+
if (raw.startsWith("/"))
|
|
44
|
+
return { ok: false, reason: "is root-relative (no leading slash)" };
|
|
45
|
+
const path = raw.normalize("NFC");
|
|
46
|
+
for (const seg of path.split("/"))
|
|
47
|
+
if (seg === "" || seg === "." || seg === "..")
|
|
48
|
+
return { ok: false, reason: "contains an empty, `.` or `..` segment" };
|
|
49
|
+
return { ok: true, path };
|
|
50
|
+
}
|
|
51
|
+
function renderSnapshotMeta(s) {
|
|
52
|
+
const meta = ["title", "description", "favicon", "label"].filter((k) => s[k] !== undefined).map((k) => `${k}: ${inlineUntrusted(s[k], 200)}`);
|
|
53
|
+
return meta.length === 0 ? "" : `${meta.join("\n")}\n`;
|
|
54
|
+
}
|
|
55
|
+
function renderFile(f) {
|
|
56
|
+
const head = `--- ${inlineUntrusted(f.path, 200)} (${inlineUntrusted(f.mediaType ?? "application/octet-stream", 80)}, ${String(f.content.byteLength).slice(0, 20)} bytes) ---`;
|
|
57
|
+
return isTextMedia(f.mediaType) ? `${head}\n${Buffer.from(f.content).toString("utf8")}` : `${head}\n[binary content, not shown]`;
|
|
58
|
+
}
|
|
59
|
+
const isRecord = (x) => x !== null && typeof x === "object";
|
|
60
|
+
const everyItem = (x, pred) => Array.isArray(x) && x.every(pred);
|
|
61
|
+
const optString = (v) => v === undefined || typeof v === "string";
|
|
62
|
+
const isRef = (x) => isRecord(x) && typeof x["url"] === "string" && typeof x["slug"] === "string" && typeof x["version"] === "string" && optString(x["title"]);
|
|
63
|
+
const isFile = (x) => isRecord(x) && typeof x["path"] === "string" && x["content"] instanceof Uint8Array && optString(x["mediaType"]);
|
|
64
|
+
const isSnapshot = (x) => isRecord(x) && everyItem(x["files"], isFile) && optString(x["title"]) && optString(x["description"]) && optString(x["favicon"]) && optString(x["label"]);
|
|
65
|
+
const failureWord = (x, allowed) => isRecord(x) && typeof x["code"] === "string" && allowed.includes(x["code"]);
|
|
66
|
+
const OUTCOME_SHAPE = {
|
|
67
|
+
publish: (o) => isRecord(o) &&
|
|
68
|
+
(o["ok"] === true
|
|
69
|
+
? isRef(o["ref"])
|
|
70
|
+
: o["ok"] === false &&
|
|
71
|
+
(o["code"] === "conflict"
|
|
72
|
+
? typeof o["currentVersion"] === "string" && (o["current"] === "reread" || isSnapshot(o["current"]))
|
|
73
|
+
: failureWord(o, ["url_foreign", "forbidden", "payload_too_large", "not_found", "force_refused", "refused"]) && typeof o["message"] === "string")),
|
|
74
|
+
list: (o) => isRecord(o) &&
|
|
75
|
+
(o["ok"] === true
|
|
76
|
+
? everyItem(o["rows"], (r) => isRef(r) && isRecord(r) && (r["owner"] === "mine" || r["owner"] === "shared") && typeof r["updatedAt"] === "string") && typeof o["truncated"] === "boolean"
|
|
77
|
+
: o["ok"] === false && failureWord(o, ["refused"]) && typeof o["message"] === "string"),
|
|
78
|
+
read: (o) => isRecord(o) && (o["ok"] === true ? isRef(o["ref"]) && typeof o["content"] === "string" && typeof o["isolatedSummary"] === "boolean" : o["ok"] === false && failureWord(o, ["not_found", "forbidden"]) && typeof o["message"] === "string"),
|
|
79
|
+
list_files: (o) => isRecord(o) && (o["ok"] === true ? everyItem(o["entries"], (e) => isRecord(e) && typeof e["path"] === "string" && typeof e["bytes"] === "number" && optString(e["mediaType"])) : o["ok"] === false && failureWord(o, ["not_found", "forbidden"]) && typeof o["message"] === "string"),
|
|
80
|
+
read_file: (o) => isRecord(o) && (o["ok"] === true ? isFile(o["file"]) : o["ok"] === false && failureWord(o, ["not_found", "forbidden"]) && typeof o["message"] === "string"),
|
|
81
|
+
};
|
|
82
|
+
function callHost(action, fn, signal) {
|
|
83
|
+
if (signal?.aborted)
|
|
84
|
+
return Promise.resolve({ ok: false, kind: "aborted", message: "the call was aborted before the host was reached" });
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
const onAbort = () => resolve({ ok: false, kind: "aborted", message: "the call was aborted while the host was working" });
|
|
87
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
88
|
+
let p;
|
|
89
|
+
try {
|
|
90
|
+
p = Promise.resolve(fn());
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
signal?.removeEventListener("abort", onAbort);
|
|
94
|
+
resolve({ ok: false, kind: "threw", message: err instanceof Error ? err.message : String(err) });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
p.then((value) => resolve(OUTCOME_SHAPE[action](value) ? { ok: true, value } : { ok: false, kind: "malformed", message: `the ${action} answer was outside the host contract` }), (err) => resolve({ ok: false, kind: "threw", message: err instanceof Error ? err.message : String(err) })).finally(() => signal?.removeEventListener("abort", onAbort));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function buildSchema(caps) {
|
|
101
|
+
const has = (a) => caps.actions.includes(a);
|
|
102
|
+
const props = {
|
|
103
|
+
action: Type.Optional(Type.Unsafe({ type: "string", enum: [...caps.actions], description: actionDescription(caps.actions) })),
|
|
104
|
+
file_path: Type.Optional(Type.String({ description: PARAM_TEXT.filePath })),
|
|
105
|
+
url: Type.Optional(Type.String({ maxLength: 2048, description: urlDescription(caps.actions) })),
|
|
106
|
+
title: Type.Optional(Type.String({ maxLength: 200, description: PARAM_TEXT.title })),
|
|
107
|
+
description: Type.Optional(Type.String({ maxLength: 1000, description: PARAM_TEXT.description })),
|
|
108
|
+
favicon: Type.Optional(Type.String({ maxLength: 32, description: PARAM_TEXT.favicon })),
|
|
109
|
+
label: Type.Optional(Type.String({ maxLength: 60, description: PARAM_TEXT.label })),
|
|
110
|
+
baseVersion: Type.Optional(Type.String({ maxLength: 200, description: PARAM_TEXT.baseVersion })),
|
|
111
|
+
force: Type.Optional(Type.Boolean({ description: PARAM_TEXT.force })),
|
|
112
|
+
publishContext: Type.Optional(Type.Unsafe({ type: "string", enum: [...ARTIFACT_PUBLISH_CONTEXTS], description: PARAM_TEXT.publishContext })),
|
|
113
|
+
};
|
|
114
|
+
if (caps.multiFile) {
|
|
115
|
+
props["files"] = Type.Optional(Type.Array(Type.Object({ path: Type.String(), file_path: Type.String() }), { maxItems: MAX_FILES_PER_PUBLISH, description: PARAM_TEXT.files }));
|
|
116
|
+
}
|
|
117
|
+
if (has("list")) {
|
|
118
|
+
props["scope"] = Type.Optional(Type.Unsafe({ type: "string", enum: [...ARTIFACT_LIST_SCOPES], description: PARAM_TEXT.scope }));
|
|
119
|
+
props["limit"] = Type.Optional(Type.Integer({ minimum: 1, maximum: LIST_LIMIT_MAX, description: PARAM_TEXT.limit }));
|
|
120
|
+
}
|
|
121
|
+
if (has("read_file"))
|
|
122
|
+
props["path"] = Type.Optional(Type.String({ maxLength: 1024, description: PARAM_TEXT.path }));
|
|
123
|
+
return Type.Object(props);
|
|
124
|
+
}
|
|
125
|
+
const invalid = (text) => errorResult(`Error (Artifact): ${text}`, { type: "artifact", invalidArguments: true });
|
|
126
|
+
const typed = (code, text, extra = {}) => errorResult(`Error (Artifact): ${text}`, { type: "artifact", code: ARTIFACT_FAILURE_RESULT_CODE[code], ...extra });
|
|
127
|
+
export function createArtifactTool(opts) {
|
|
128
|
+
const { host, capabilities: caps, runId, taskId, sessionId, agentName, readLocalFile } = opts;
|
|
129
|
+
const versions = new Map();
|
|
130
|
+
const bindings = new Map();
|
|
131
|
+
const occurrences = new Map();
|
|
132
|
+
const hasAction = (a) => caps.actions.includes(a);
|
|
133
|
+
const rememberRef = (ref) => {
|
|
134
|
+
versions.set(ref.url, ref.version);
|
|
135
|
+
};
|
|
136
|
+
const attemptIdOf = (ctx) => {
|
|
137
|
+
const n = occurrences.get(ctx.toolCallId) ?? 0;
|
|
138
|
+
occurrences.set(ctx.toolCallId, n + 1);
|
|
139
|
+
return artifactAttemptId(runId, ctx.toolCallId, n);
|
|
140
|
+
};
|
|
141
|
+
const contextFor = (attemptId) => ({
|
|
142
|
+
taskId,
|
|
143
|
+
...(sessionId !== undefined ? { sessionId } : {}),
|
|
144
|
+
...(agentName !== undefined ? { agentName } : {}),
|
|
145
|
+
runId,
|
|
146
|
+
attemptId,
|
|
147
|
+
});
|
|
148
|
+
const hostError = (verb, fault, url) => {
|
|
149
|
+
if (caps.plane === "local-stub" && fault.kind === "threw") {
|
|
150
|
+
return errorResult(`Error (Artifact): the local artifact stub failed before committing (${inlineUntrusted(fault.message, 200)}). Nothing was published; the call can be repeated.`, {
|
|
151
|
+
type: "artifact", code: ARTIFACT_HOST_ERROR_CODE, outcome: "not_executed", plane: caps.plane,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const verifiable = hasAction("read") && url !== "";
|
|
155
|
+
return errorResult(`Error (Artifact): the artifact host did not answer the ${verb.slice(0, 20)} (${inlineUntrusted(fault.message, 200)}). Whether it was applied is not known.${verifiable ? ` Verify with action: "read" on ${url.slice(0, 200)} before repeating the call.` : ""}`, {
|
|
156
|
+
type: "artifact", code: ARTIFACT_HOST_ERROR_CODE, outcome: "unknown", plane: caps.plane,
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
const notFoundOrForbidden = (code, url, message) => code === "not_found"
|
|
160
|
+
? typed(code, `no artifact at ${inlineUntrusted(url, 200)} (${inlineUntrusted(message, 200)}).`, { url })
|
|
161
|
+
: typed(code, `${inlineUntrusted(url, 200)} is not accessible to this session (${inlineUntrusted(message, 200)}).`, { url });
|
|
162
|
+
const readRefusal = (path, r) => {
|
|
163
|
+
switch (r.reason) {
|
|
164
|
+
case "refused":
|
|
165
|
+
return errorResult(r.text.slice(0, 4000), r.details);
|
|
166
|
+
case "too_large":
|
|
167
|
+
return typed("payload_too_large", `${inlineUntrusted(path, 200)} is ${String(r.size).slice(0, 20)} bytes; this host accepts at most ${String(r.limit).slice(0, 20)} bytes for it${r.judgedOn === "metadata" ? " (the file was not read)" : " (it grew past its declared size while being read)"}.`, { limit: r.limit, measured: r.size, judgedOn: r.judgedOn });
|
|
168
|
+
case "not_a_file":
|
|
169
|
+
return invalid(`${inlineUntrusted(path, 200)} is a ${inlineUntrusted(r.kind, 20)}, not a regular file.`);
|
|
170
|
+
default: {
|
|
171
|
+
const _exhaustive = r;
|
|
172
|
+
return _exhaustive;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
async function publish(a, ctx) {
|
|
177
|
+
const attemptId = attemptIdOf(ctx);
|
|
178
|
+
if (a.file_path === undefined)
|
|
179
|
+
return invalid("`file_path` is required to publish.");
|
|
180
|
+
const files = [];
|
|
181
|
+
const budget = Math.min(caps.maxFileBytes, caps.maxTotalBytes);
|
|
182
|
+
const first = await readLocalFile(a.file_path, budget, ctx.signal);
|
|
183
|
+
if (!first.ok)
|
|
184
|
+
return readRefusal(a.file_path, first);
|
|
185
|
+
files.push({ path: basenameOf(a.file_path), content: first.bytes, mediaType: mediaTypeOf(a.file_path) });
|
|
186
|
+
let total = first.bytes.byteLength;
|
|
187
|
+
const url = a.url ?? bindings.get(first.key);
|
|
188
|
+
if (url === undefined && (a.force === true || a.baseVersion !== undefined)) {
|
|
189
|
+
return invalid("`force` and `baseVersion` only apply to an update — pass `url`, or republish the same file path this session already published.");
|
|
190
|
+
}
|
|
191
|
+
if (a.files !== undefined && a.files.length > 0) {
|
|
192
|
+
if (!caps.multiFile)
|
|
193
|
+
return invalid("this artifact host publishes single files only; `files` is not accepted.");
|
|
194
|
+
const seen = new Set([files[0].path.normalize("NFC").toLowerCase()]);
|
|
195
|
+
for (const entry of a.files) {
|
|
196
|
+
const norm = normalizePublishedPath(entry.path);
|
|
197
|
+
if (!norm.ok)
|
|
198
|
+
return invalid(`files entry ${inlineUntrusted(entry.path, 120)} ${norm.reason.slice(0, 80)}.`);
|
|
199
|
+
const fold = norm.path.toLowerCase();
|
|
200
|
+
if (seen.has(fold))
|
|
201
|
+
return invalid(`files entry ${inlineUntrusted(norm.path, 120)} collides with another published path.`);
|
|
202
|
+
seen.add(fold);
|
|
203
|
+
const left = caps.maxTotalBytes - total;
|
|
204
|
+
if (left <= 0)
|
|
205
|
+
return typed("payload_too_large", `the files already total ${String(total).slice(0, 20)} bytes; this host accepts at most ${String(caps.maxTotalBytes).slice(0, 20)} bytes per version.`, { limit: caps.maxTotalBytes, measured: total });
|
|
206
|
+
const r = await readLocalFile(entry.file_path, Math.min(caps.maxFileBytes, left), ctx.signal);
|
|
207
|
+
if (!r.ok)
|
|
208
|
+
return readRefusal(entry.file_path, r);
|
|
209
|
+
total += r.bytes.byteLength;
|
|
210
|
+
files.push({ path: norm.path, content: r.bytes, mediaType: mediaTypeOf(norm.path) });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const baseVersion = a.baseVersion ?? (url !== undefined ? versions.get(url) : undefined);
|
|
214
|
+
const req = {
|
|
215
|
+
...(url !== undefined ? { url } : {}),
|
|
216
|
+
files,
|
|
217
|
+
...(a.title !== undefined ? { title: a.title } : {}),
|
|
218
|
+
...(a.description !== undefined ? { description: a.description } : {}),
|
|
219
|
+
...(a.favicon !== undefined ? { favicon: a.favicon } : {}),
|
|
220
|
+
...(a.label !== undefined ? { label: a.label } : {}),
|
|
221
|
+
...(baseVersion !== undefined ? { baseVersion } : {}),
|
|
222
|
+
...(a.force === true ? { force: true } : {}),
|
|
223
|
+
publishContext: a.publishContext ?? "plain",
|
|
224
|
+
};
|
|
225
|
+
const call = await callHost("publish", () => host.publish(req, contextFor(attemptId)), ctx.signal);
|
|
226
|
+
if (!call.ok)
|
|
227
|
+
return hostError("publish", call, inlineUntrusted(url ?? "", 200));
|
|
228
|
+
const out = call.value;
|
|
229
|
+
if (out.ok) {
|
|
230
|
+
rememberRef(out.ref);
|
|
231
|
+
bindings.set(first.key, out.ref.url);
|
|
232
|
+
const title = out.ref.title !== undefined ? ` — ${inlineUntrusted(out.ref.title, 120)}` : "";
|
|
233
|
+
return {
|
|
234
|
+
content: `Published ${inlineUntrusted(out.ref.url, 300)} (version ${inlineUntrusted(out.ref.version, 60)})${title}.`,
|
|
235
|
+
details: { type: "artifact", action: "publish", plane: caps.plane, ref: out.ref, update: url !== undefined },
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (out.code === "conflict") {
|
|
239
|
+
const snapshot = out.current;
|
|
240
|
+
if (snapshot !== "reread" && url !== undefined)
|
|
241
|
+
versions.set(url, out.currentVersion);
|
|
242
|
+
const handedBack = snapshot === "reread"
|
|
243
|
+
? hasAction("read")
|
|
244
|
+
? `The current content was not handed back; read it with action: "read" on ${inlineUntrusted(url ?? "", 200)}.`
|
|
245
|
+
: "The current content was not handed back by the host."
|
|
246
|
+
: `Current content follows.\n${renderSnapshotMeta(snapshot)}${snapshot.files.map(renderFile).join("\n")}`;
|
|
247
|
+
return errorResult(`Error (Artifact): publish conflict — ${inlineUntrusted(url ?? "", 200)} is at version ${inlineUntrusted(out.currentVersion, 60)}, this publish was based on ${inlineUntrusted(baseVersion ?? "", 60)}. Merge your changes onto the current content and publish again. ${handedBack}`, { type: "artifact", code: ARTIFACT_FAILURE_RESULT_CODE.conflict, url, currentVersion: out.currentVersion, current: snapshot === "reread" ? "reread" : { ...snapshot, files: snapshot.files.map((f) => ({ path: f.path, bytes: f.content.byteLength, mediaType: f.mediaType })) } });
|
|
248
|
+
}
|
|
249
|
+
switch (out.code) {
|
|
250
|
+
case "url_foreign":
|
|
251
|
+
return typed(out.code, `${inlineUntrusted(url ?? "", 200)} is not an artifact this host (or this run) published; nothing was published (${inlineUntrusted(out.message, 200)}).`, { url });
|
|
252
|
+
case "forbidden":
|
|
253
|
+
return typed(out.code, `${inlineUntrusted(url ?? "", 200)} is not this session's to update; nothing was published (${inlineUntrusted(out.message, 200)}).`, { url });
|
|
254
|
+
case "payload_too_large":
|
|
255
|
+
return typed(out.code, `the host refused the payload as too large; nothing was published (${inlineUntrusted(out.message, 200)}).`);
|
|
256
|
+
case "not_found":
|
|
257
|
+
return typed(out.code, `no artifact at ${inlineUntrusted(url ?? "", 200)}; nothing was published (${inlineUntrusted(out.message, 200)}).`, { url });
|
|
258
|
+
case "force_refused":
|
|
259
|
+
return typed(out.code, `the host refused to overwrite with force; nothing was published (${inlineUntrusted(out.message, 200)}). Merge onto the current version instead.`, { url });
|
|
260
|
+
case "refused":
|
|
261
|
+
return typed(out.code, `the host refused the publish; nothing was published (${inlineUntrusted(out.message, 200)}).`);
|
|
262
|
+
default: {
|
|
263
|
+
const _exhaustive = out;
|
|
264
|
+
return _exhaustive;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async function list(a, ctx) {
|
|
269
|
+
const attemptId = attemptIdOf(ctx);
|
|
270
|
+
const scope = a.scope ?? "mine";
|
|
271
|
+
const limit = a.limit ?? LIST_LIMIT_DEFAULT;
|
|
272
|
+
const call = await callHost("list", () => host.list({ scope, limit }, contextFor(attemptId)), ctx.signal);
|
|
273
|
+
if (!call.ok)
|
|
274
|
+
return hostError("list", call, "");
|
|
275
|
+
const out = call.value;
|
|
276
|
+
if (!out.ok)
|
|
277
|
+
return typed("refused", `the host refused the listing (${inlineUntrusted(out.message, 200)}).`);
|
|
278
|
+
const lines = out.rows.map((r) => `${inlineUntrusted(r.url, 300)} v${inlineUntrusted(r.version, 40)} ${inlineUntrusted(r.updatedAt, 40)} (${r.owner})${r.title !== undefined ? ` ${inlineUntrusted(r.title, 120)}` : ""}`);
|
|
279
|
+
const body = lines.length === 0 ? "nothing listed" : lines.join("\n");
|
|
280
|
+
return { content: `${body}${out.truncated ? "\n(more artifacts exist; raise `limit` to see them)" : ""}`, details: { type: "artifact", action: "list", plane: caps.plane, scope, rows: out.rows, truncated: out.truncated } };
|
|
281
|
+
}
|
|
282
|
+
async function read(a, ctx) {
|
|
283
|
+
const attemptId = attemptIdOf(ctx);
|
|
284
|
+
if (a.url === undefined)
|
|
285
|
+
return invalid("`url` is required to read an artifact.");
|
|
286
|
+
const call = await callHost("read", () => host.read({ url: a.url }, contextFor(attemptId)), ctx.signal);
|
|
287
|
+
if (!call.ok)
|
|
288
|
+
return hostError("read", call, inlineUntrusted(a.url, 200));
|
|
289
|
+
const out = call.value;
|
|
290
|
+
if (!out.ok)
|
|
291
|
+
return notFoundOrForbidden(out.code, a.url, out.message);
|
|
292
|
+
rememberRef(out.ref);
|
|
293
|
+
const head = out.isolatedSummary ? `[isolated summary of a shared artifact — ${inlineUntrusted(out.ref.url, 300)}, version ${inlineUntrusted(out.ref.version, 60)}]\n` : "";
|
|
294
|
+
return { content: `${head}${out.content}`, details: { type: "artifact", action: "read", plane: caps.plane, ref: out.ref, isolatedSummary: out.isolatedSummary } };
|
|
295
|
+
}
|
|
296
|
+
async function listFiles(a, ctx) {
|
|
297
|
+
const attemptId = attemptIdOf(ctx);
|
|
298
|
+
if (a.url === undefined)
|
|
299
|
+
return invalid("`url` is required to list an artifact's files.");
|
|
300
|
+
const call = await callHost("list_files", () => host.listFiles({ url: a.url }, contextFor(attemptId)), ctx.signal);
|
|
301
|
+
if (!call.ok)
|
|
302
|
+
return hostError("list_files", call, inlineUntrusted(a.url, 200));
|
|
303
|
+
const out = call.value;
|
|
304
|
+
if (!out.ok)
|
|
305
|
+
return notFoundOrForbidden(out.code, a.url, out.message);
|
|
306
|
+
const lines = out.entries.map((e) => `${inlineUntrusted(e.path, 200)} ${String(e.bytes).slice(0, 20)} bytes${e.mediaType !== undefined ? ` ${inlineUntrusted(e.mediaType, 80)}` : ""}`);
|
|
307
|
+
return { content: lines.length === 0 ? "no files" : lines.join("\n"), details: { type: "artifact", action: "list_files", plane: caps.plane, url: a.url, entries: out.entries } };
|
|
308
|
+
}
|
|
309
|
+
async function readFile(a, ctx) {
|
|
310
|
+
const attemptId = attemptIdOf(ctx);
|
|
311
|
+
if (a.url === undefined || a.path === undefined)
|
|
312
|
+
return invalid("`url` and `path` are required to read a published file.");
|
|
313
|
+
const call = await callHost("read_file", () => host.readFile({ url: a.url, path: a.path }, contextFor(attemptId)), ctx.signal);
|
|
314
|
+
if (!call.ok)
|
|
315
|
+
return hostError("read_file", call, inlineUntrusted(a.url, 200));
|
|
316
|
+
const out = call.value;
|
|
317
|
+
if (!out.ok)
|
|
318
|
+
return notFoundOrForbidden(out.code, a.url, out.message);
|
|
319
|
+
return { content: renderFile(out.file), details: { type: "artifact", action: "read_file", plane: caps.plane, url: a.url, path: out.file.path, bytes: out.file.content.byteLength, mediaType: out.file.mediaType } };
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
name: ARTIFACT_TOOL_NAME,
|
|
323
|
+
...toolFace("artifact"),
|
|
324
|
+
contentOrigin: ARTIFACT_PLANE_CONTENT_ORIGIN[caps.plane],
|
|
325
|
+
description: artifactDescription(caps.plane, caps.actions),
|
|
326
|
+
parameters: buildSchema(caps),
|
|
327
|
+
prepareArguments: (args) => (isRecord(args) && args["action"] === undefined ? { ...args, action: "publish" } : args),
|
|
328
|
+
execute: (args, ctx) => {
|
|
329
|
+
const a = args;
|
|
330
|
+
const action = a.action ?? "publish";
|
|
331
|
+
if (!hasAction(action))
|
|
332
|
+
return invalid(`action ${inlineUntrusted(String(action), 40)} is not available on this artifact host.`);
|
|
333
|
+
switch (action) {
|
|
334
|
+
case "publish":
|
|
335
|
+
return publish(a, ctx);
|
|
336
|
+
case "list":
|
|
337
|
+
return list(a, ctx);
|
|
338
|
+
case "read":
|
|
339
|
+
return read(a, ctx);
|
|
340
|
+
case "list_files":
|
|
341
|
+
return listFiles(a, ctx);
|
|
342
|
+
case "read_file":
|
|
343
|
+
return readFile(a, ctx);
|
|
344
|
+
default: {
|
|
345
|
+
const _exhaustive = action;
|
|
346
|
+
return _exhaustive;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ArtifactCallContext, type ArtifactHost, type ArtifactHostCapabilities, type ArtifactPublishOutcome, type ArtifactPublishRequest, type ArtifactRef } from "../../core/artifact-host.js";
|
|
2
|
+
export declare const EVAL_STUB_URL_PREFIX = "eval-stub://artifact/";
|
|
3
|
+
export declare const LOCAL_STUB_MAX_FILE_BYTES: number;
|
|
4
|
+
export declare const LOCAL_STUB_MAX_TOTAL_BYTES: number;
|
|
5
|
+
export interface LocalArtifactStubOptions {
|
|
6
|
+
/** The storage directory. Required: the library never guesses a location on the host filesystem. Contract: it must
|
|
7
|
+
* lie OUTSIDE every root the run may write or read (the task root, `additionalDirectories`,
|
|
8
|
+
* `additionalReadDirectories`); the engine refuses to mount a stub whose store is inside one — a ledger the model
|
|
9
|
+
* can edit through the fs tools is not a trust boundary. */
|
|
10
|
+
dir: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class LocalArtifactStub implements ArtifactHost {
|
|
13
|
+
#private;
|
|
14
|
+
readonly dir: string;
|
|
15
|
+
constructor(opts: LocalArtifactStubOptions);
|
|
16
|
+
capabilities(): ArtifactHostCapabilities;
|
|
17
|
+
onMount(runId: string): void;
|
|
18
|
+
/** The slugs a run may name through `url` (inspection; a copy). */
|
|
19
|
+
ownMinted(runId: string): ReadonlySet<string>;
|
|
20
|
+
publish(req: ArtifactPublishRequest, ctx: ArtifactCallContext): Promise<ArtifactPublishOutcome>;
|
|
21
|
+
/** The ledger rows (inspection for hosts and tests; a copy). */
|
|
22
|
+
rows(): ReadonlyArray<ArtifactRef>;
|
|
23
|
+
/** The published paths of one version (inspection). */
|
|
24
|
+
versionFiles(slug: string, version: number): string[];
|
|
25
|
+
}
|
|
26
|
+
export declare function localArtifactStub(opts: LocalArtifactStubOptions): LocalArtifactStub;
|