@sema-agent/core 5.47.0 → 5.48.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 +52 -0
- package/dist/agents/agent-transcript-tool.d.ts +4 -0
- package/dist/agents/agent-transcript-tool.js +10 -3
- package/dist/agents/send-message-tool.d.ts +43 -1
- package/dist/agents/send-message-tool.js +50 -11
- package/dist/agents/subagent.d.ts +18 -0
- package/dist/agents/subagent.js +102 -2
- package/dist/config/defaults.d.ts +20 -0
- package/dist/config/defaults.js +5 -0
- package/dist/core/background-agent-store.d.ts +1 -0
- package/dist/core/background-agent-store.js +13 -0
- package/dist/core/mcp.d.ts +6 -1
- package/dist/core/mcp.js +34 -7
- package/dist/core/reminder-disclosure.d.ts +90 -0
- package/dist/core/reminder-disclosure.js +64 -0
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +6 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -0
- package/dist/core/runner/prepare-hands-readface.js +1 -0
- package/dist/core/runner/prepare-task.d.ts +15 -0
- package/dist/core/runner/prepare-task.js +48 -30
- package/dist/core/runner/runtask.js +3 -1
- package/dist/core/session-store.d.ts +59 -1
- package/dist/core/session-store.js +82 -14
- package/dist/core/session.d.ts +83 -1
- package/dist/core/task-registry-agent.d.ts +28 -0
- package/dist/core/task-registry-agent.js +63 -2
- package/dist/core/task-registry.d.ts +21 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/types.d.ts +60 -0
- package/dist/core/untrusted-text.d.ts +63 -0
- package/dist/core/untrusted-text.js +48 -0
- package/dist/core/wiring-manifest.d.ts +35 -0
- package/dist/core/wiring-manifest.js +21 -1
- package/dist/engine/harness/types.d.ts +36 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +4 -3
- package/dist/internal/harness-types.d.ts +1 -0
- package/dist/stores/file/index.d.ts +19 -3
- package/dist/stores/file/index.js +24 -1
- package/dist/stores/file/session-store.d.ts +18 -4
- package/dist/stores/file/session-store.js +73 -12
- package/dist/tools/fs/fs-pdf.d.ts +12 -1
- package/dist/tools/fs/fs-pdf.js +17 -3
- package/dist/tools/fs/fs-read.d.ts +2 -1
- package/dist/tools/fs/fs-read.js +33 -5
- package/dist/tools/fs/fs-shared.d.ts +6 -2
- package/dist/tools/fs/index.d.ts +7 -0
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/web.js +21 -2
- package/package.json +3 -2
- package/test/export-surface.snapshot.json +15 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { BaseSessionStorage, StoredSession, SessionError, getEntriesToFork, uuidv7, validateEntriesForImport, } from "../../internal/harness.js";
|
|
4
4
|
import { canonicalStoreKey, AppendLog, atomicWriteFile, ensureDir, readJsonlRecords, sanitizePathComponent } from "./fs-atomic.js";
|
|
@@ -83,18 +83,20 @@ export class FileSessionRepo {
|
|
|
83
83
|
const lines = readJsonlRecords(path, (info) => this.disclose(info.path, info.reason));
|
|
84
84
|
let createdAt = "";
|
|
85
85
|
let forkedFrom;
|
|
86
|
+
let placement;
|
|
86
87
|
const entries = [];
|
|
87
88
|
for (const line of lines) {
|
|
88
89
|
if (line.kind === "meta") {
|
|
89
90
|
createdAt = line.createdAt;
|
|
90
91
|
forkedFrom = line.forkedFrom;
|
|
92
|
+
placement = line.placement;
|
|
91
93
|
}
|
|
92
94
|
else
|
|
93
95
|
entries.push(line.entry);
|
|
94
96
|
}
|
|
95
|
-
return { createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), entries };
|
|
97
|
+
return { createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), ...(placement !== undefined ? { placement } : {}), entries };
|
|
96
98
|
}
|
|
97
|
-
storage(id, createdAt, entries, forkedFrom) {
|
|
99
|
+
storage(id, createdAt, entries, forkedFrom, placement) {
|
|
98
100
|
const canonical = canonicalStoreKey(this.pathFor(id));
|
|
99
101
|
const live = sharedSessionStorages.get(canonical)?.deref();
|
|
100
102
|
if (live !== undefined) {
|
|
@@ -103,7 +105,7 @@ export class FileSessionRepo {
|
|
|
103
105
|
return live;
|
|
104
106
|
}
|
|
105
107
|
const log = new AppendLog(this.pathFor(id));
|
|
106
|
-
const created = new FileSessionStorage(log, { id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}) }, entries, canonical);
|
|
108
|
+
const created = new FileSessionStorage(log, { id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}), ...(placement !== undefined ? { placement } : {}) }, entries, canonical);
|
|
107
109
|
sharedSessionStorages.set(canonical, new WeakRef(created));
|
|
108
110
|
created.addHolder(this);
|
|
109
111
|
this.joined.set(canonical, new WeakRef(created));
|
|
@@ -128,15 +130,16 @@ export class FileSessionRepo {
|
|
|
128
130
|
const path = this.pathFor(id);
|
|
129
131
|
if (!existsSync(path)) {
|
|
130
132
|
evictSharedSessionStorage(canonicalStoreKey(path));
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
const placement = options.placement !== undefined ? { ...options.placement, placedAt: options.placement.placedAt ?? Date.now() } : undefined;
|
|
134
|
+
atomicWriteFile(this.tmpDir, path, `${JSON.stringify({ kind: "meta", id, createdAt, ...(placement !== undefined ? { placement } : {}) })}\n`);
|
|
135
|
+
return new StoredSession(this.storage(id, createdAt, [], undefined, placement));
|
|
133
136
|
}
|
|
134
|
-
const { createdAt: existingCreatedAt, forkedFrom, entries } = this.read(id);
|
|
135
|
-
return new StoredSession(this.storage(id, existingCreatedAt || createdAt, entries, forkedFrom));
|
|
137
|
+
const { createdAt: existingCreatedAt, forkedFrom, placement, entries } = this.read(id);
|
|
138
|
+
return new StoredSession(this.storage(id, existingCreatedAt || createdAt, entries, forkedFrom, placement));
|
|
136
139
|
}
|
|
137
140
|
async open(metadata) {
|
|
138
|
-
const { createdAt, forkedFrom, entries } = this.read(metadata.id);
|
|
139
|
-
return new StoredSession(this.storage(metadata.id, createdAt, entries, forkedFrom));
|
|
141
|
+
const { createdAt, forkedFrom, placement, entries } = this.read(metadata.id);
|
|
142
|
+
return new StoredSession(this.storage(metadata.id, createdAt, entries, forkedFrom, placement));
|
|
140
143
|
}
|
|
141
144
|
async list() {
|
|
142
145
|
let names;
|
|
@@ -155,7 +158,9 @@ export class FileSessionRepo {
|
|
|
155
158
|
continue;
|
|
156
159
|
const id = name.slice(0, -SUFFIX.length);
|
|
157
160
|
try {
|
|
158
|
-
const { createdAt, forkedFrom } = this.read(id);
|
|
161
|
+
const { createdAt, forkedFrom, placement } = this.read(id);
|
|
162
|
+
if (placement !== undefined)
|
|
163
|
+
continue;
|
|
159
164
|
out.push({ id, createdAt, ...(forkedFrom !== undefined ? { forkedFrom } : {}) });
|
|
160
165
|
}
|
|
161
166
|
catch (err) {
|
|
@@ -166,12 +171,68 @@ export class FileSessionRepo {
|
|
|
166
171
|
}
|
|
167
172
|
return out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
168
173
|
}
|
|
174
|
+
async listPlaced(kind, opts) {
|
|
175
|
+
let names;
|
|
176
|
+
try {
|
|
177
|
+
names = readdirSync(this.dir);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
if (err.code !== "ENOENT") {
|
|
181
|
+
this.disclose(this.dir, `session directory unreadable (${err.code ?? "unknown"}) — placed listing degraded to empty`);
|
|
182
|
+
}
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
const now = Date.now();
|
|
186
|
+
const out = [];
|
|
187
|
+
for (const name of names) {
|
|
188
|
+
if (!name.endsWith(SUFFIX))
|
|
189
|
+
continue;
|
|
190
|
+
const id = name.slice(0, -SUFFIX.length);
|
|
191
|
+
let placement;
|
|
192
|
+
try {
|
|
193
|
+
placement = this.read(id).placement;
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
if (!(err instanceof SessionError && err.code === "not_found")) {
|
|
197
|
+
this.disclose(this.pathFor(id), `session log unreadable (${err instanceof Error ? err.message : String(err)}) — skipped from the placed listing`);
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (placement === undefined || placement.kind !== kind)
|
|
202
|
+
continue;
|
|
203
|
+
if (opts?.scope !== undefined && placement.scope !== opts.scope)
|
|
204
|
+
continue;
|
|
205
|
+
if (opts?.olderThanMs !== undefined) {
|
|
206
|
+
let ageAnchor = placement.placedAt;
|
|
207
|
+
try {
|
|
208
|
+
ageAnchor = statSync(this.pathFor(id)).mtimeMs;
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
}
|
|
212
|
+
if (now - ageAnchor <= opts.olderThanMs)
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
out.push(placement.scope !== undefined && placement.handle !== undefined
|
|
216
|
+
? { sessionId: id, placedAt: placement.placedAt, scope: placement.scope, handle: placement.handle }
|
|
217
|
+
: { sessionId: id, placedAt: placement.placedAt, tupleIncomplete: true });
|
|
218
|
+
}
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
async placementOf(sessionId) {
|
|
222
|
+
try {
|
|
223
|
+
return this.read(sessionId).placement;
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
169
229
|
async delete(metadata) {
|
|
170
230
|
evictSharedSessionStorage(canonicalStoreKey(this.pathFor(metadata.id)));
|
|
171
231
|
try {
|
|
172
232
|
rmSync(this.pathFor(metadata.id), { force: true });
|
|
173
233
|
}
|
|
174
|
-
catch {
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new SessionError("storage", `session ${metadata.id} could not be deleted: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : undefined);
|
|
175
236
|
}
|
|
176
237
|
}
|
|
177
238
|
async fork(sourceMetadata, options = {}) {
|
|
@@ -3,6 +3,17 @@ import type { DocumentContent, ImageContent, TextContent } from "../../internal/
|
|
|
3
3
|
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
4
4
|
import { type PdfModelCapabilities } from "./pdf.js";
|
|
5
5
|
import { type ReadImageDownsamplerOption } from "./fs-shared.js";
|
|
6
|
+
import { type ReminderDisclosureCounts } from "../../core/reminder-disclosure.js";
|
|
7
|
+
/** design/319 (B ticket) — the Read tool's disclosure state, threaded into the PDF TEXT legs (the
|
|
8
|
+
* two `pdftotext` extraction arms — the only PDF returns with a model-facing text projection of
|
|
9
|
+
* the document; the native document block and rendered page images have no text to scan). Shares
|
|
10
|
+
* the Read closure's throttle windows so a PDF and its text read dedup on the same file key. */
|
|
11
|
+
export interface PdfReminderDisclosure {
|
|
12
|
+
mark: string | undefined;
|
|
13
|
+
windows: Map<string, number>;
|
|
14
|
+
key: string;
|
|
15
|
+
counts?: ReminderDisclosureCounts;
|
|
16
|
+
}
|
|
6
17
|
export { pdfModelCapabilitiesOf, type PdfModelCapabilities } from "./pdf.js";
|
|
7
18
|
/** What the Read tool's PDF pipeline returns (document block, or rendered page images, or an error string). */
|
|
8
19
|
type ReadPdfReturn = string | {
|
|
@@ -33,7 +44,7 @@ type ReadPdfReturn = string | {
|
|
|
33
44
|
* Every degraded return carries `details.fallback = { level, reason }` (telemetry on the structured frame).
|
|
34
45
|
* `caps` absent ⇒ fully capable (byte-compat: native document block; the brain placeholder still guards).
|
|
35
46
|
*/
|
|
36
|
-
export declare function readPdfFile(env: ExecutionEnv, path: string, key: string, pages: string | undefined, signal: AbortSignal | undefined, downsamplerOpt: ReadImageDownsamplerOption, cwd: string, preRead?: Uint8Array, caps?: PdfModelCapabilities, readDeny?: import("./read-deny.js").ReadDenyMatcher): Promise<ReadPdfReturn>;
|
|
47
|
+
export declare function readPdfFile(env: ExecutionEnv, path: string, key: string, pages: string | undefined, signal: AbortSignal | undefined, downsamplerOpt: ReadImageDownsamplerOption, cwd: string, preRead?: Uint8Array, caps?: PdfModelCapabilities, readDeny?: import("./read-deny.js").ReadDenyMatcher, disclosure?: PdfReminderDisclosure): Promise<ReadPdfReturn>;
|
|
37
48
|
/** E1: readPdfFile's own return type stays `ReadPdfReturn` (its INTERNAL string-means-error dispatch
|
|
38
49
|
* contract, shared with pdfPagesToImageBlocks) — the isError flag is applied once, here, at the tool's
|
|
39
50
|
* actual execute() boundary, not inside the helper. */
|
package/dist/tools/fs/fs-pdf.js
CHANGED
|
@@ -3,6 +3,20 @@ import { delimitUntrusted } from "../../core/untrusted-text.js";
|
|
|
3
3
|
import { MCP_IMAGE_MAX_BASE64 } from "../../core/mcp.js";
|
|
4
4
|
import { PDF_FALLBACK_RENDER_PAGES, PDF_INLINE_PAGE_THRESHOLD, PDF_MAX_EXTRACT_SIZE, PDF_MAX_PAGES_PER_READ, PDF_TARGET_RAW_SIZE, extractPdfPagesAsImages, extractPdfTextLayer, getPdfPageCount, parsePdfPageRange, pdfMagicMatches, } from "./pdf.js";
|
|
5
5
|
import { resolveAutoDownsampler, enoentMessage, MAX_READ_BYTES } from "./fs-shared.js";
|
|
6
|
+
import { discloseReminderShaped } from "../../core/reminder-disclosure.js";
|
|
7
|
+
function pdfDisclosureTrailer(body, d) {
|
|
8
|
+
if (d === undefined)
|
|
9
|
+
return "";
|
|
10
|
+
const out = discloseReminderShaped({
|
|
11
|
+
segments: [body],
|
|
12
|
+
mark: d.mark,
|
|
13
|
+
outlet: "pdf",
|
|
14
|
+
defuseExactMark: false,
|
|
15
|
+
throttle: { key: d.key, windows: d.windows },
|
|
16
|
+
counts: d.counts,
|
|
17
|
+
});
|
|
18
|
+
return out.trailer !== undefined ? `\n${out.trailer}` : "";
|
|
19
|
+
}
|
|
6
20
|
export { pdfModelCapabilitiesOf } from "./pdf.js";
|
|
7
21
|
async function pdfPagesToImageBlocks(pages, path, downsamplerOpt) {
|
|
8
22
|
const downsampler = downsamplerOpt === false ? undefined : (downsamplerOpt ?? (await resolveAutoDownsampler()));
|
|
@@ -30,7 +44,7 @@ function boundPdfExtractedText(text) {
|
|
|
30
44
|
return { body: text, truncated: false };
|
|
31
45
|
return { body: text.slice(0, MAX_READ_BYTES), truncated: true };
|
|
32
46
|
}
|
|
33
|
-
export async function readPdfFile(env, path, key, pages, signal, downsamplerOpt, cwd, preRead, caps, readDeny) {
|
|
47
|
+
export async function readPdfFile(env, path, key, pages, signal, downsamplerOpt, cwd, preRead, caps, readDeny, disclosure) {
|
|
34
48
|
const cap = caps ?? { document: true, vision: true };
|
|
35
49
|
const meta = await env.fileInfo(key, signal);
|
|
36
50
|
if (!meta.ok) {
|
|
@@ -83,7 +97,7 @@ export async function readPdfFile(env, path, key, pages, signal, downsamplerOpt,
|
|
|
83
97
|
{
|
|
84
98
|
type: "text",
|
|
85
99
|
text: `[PDF: ${path} — pages ${range.first}-${last}${pageCount !== undefined ? ` of ${pageCount}` : ""} extracted as text via pdftotext (the serving model does not support native PDF input)${timeoutNote}]` +
|
|
86
|
-
`\n${delimitUntrusted("PDF text layer", body)}${truncated ? `\n[extracted text truncated at ${MAX_READ_BYTES} bytes; narrow the pages range]` : ""}`,
|
|
100
|
+
`\n${delimitUntrusted("PDF text layer", body)}${truncated ? `\n[extracted text truncated at ${MAX_READ_BYTES} bytes; narrow the pages range]` : ""}${pdfDisclosureTrailer(body, disclosure)}`,
|
|
87
101
|
},
|
|
88
102
|
],
|
|
89
103
|
details: {
|
|
@@ -166,7 +180,7 @@ export async function readPdfFile(env, path, key, pages, signal, downsamplerOpt,
|
|
|
166
180
|
{
|
|
167
181
|
type: "text",
|
|
168
182
|
text: `[PDF: ${path} — extracted text layer via pdftotext${pageCount !== undefined ? `; ${pageCount} page${pageCount === 1 ? "" : "s"}` : ""} (the serving model does not support native PDF input)${timeoutNote}]` +
|
|
169
|
-
`\n${delimitUntrusted("PDF text layer", body)}${truncated ? `\n[extracted text truncated at ${MAX_READ_BYTES} bytes; use the pages parameter to read specific ranges]` : ""}`,
|
|
183
|
+
`\n${delimitUntrusted("PDF text layer", body)}${truncated ? `\n[extracted text truncated at ${MAX_READ_BYTES} bytes; use the pages parameter to read specific ranges]` : ""}${pdfDisclosureTrailer(body, disclosure)}`,
|
|
170
184
|
},
|
|
171
185
|
],
|
|
172
186
|
details: {
|
|
@@ -2,7 +2,8 @@ import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
|
|
|
2
2
|
import { type ReadFileState } from "./safety.js";
|
|
3
3
|
import { type PdfModelCapabilities } from "./pdf.js";
|
|
4
4
|
import { type ReadImageDownsamplerOption, type CwdRef } from "./fs-shared.js";
|
|
5
|
+
import { type ReminderDisclosureCounts } from "../../core/reminder-disclosure.js";
|
|
5
6
|
export declare function createReadFileTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: readonly string[], imageDownsampler?: ReadImageDownsamplerOption, pdfCapabilities?: PdfModelCapabilities, bgOutputReadExemption?: (canonicalKey: string, ctx: {
|
|
6
7
|
taskId?: string;
|
|
7
8
|
principal?: string;
|
|
8
|
-
}) => boolean, readCyberReminder?: boolean, readDeny?: import("./read-deny.js").ReadDenyMatcher, readFace?: import("./read-face.js").ReadFace, reminderMark?: string): AgentTool;
|
|
9
|
+
}) => boolean, readCyberReminder?: boolean, readDeny?: import("./read-deny.js").ReadDenyMatcher, readFace?: import("./read-face.js").ReadFace, reminderMark?: string, reminderDisclosureCounts?: ReminderDisclosureCounts): AgentTool;
|
package/dist/tools/fs/fs-read.js
CHANGED
|
@@ -8,8 +8,16 @@ import { PDF_MAX_PAGES_PER_READ, pdfMagicMatches } from "./pdf.js";
|
|
|
8
8
|
import { MAX_READ_BYTES, SLICED_READ_MAX_BYTES, MAX_IMAGE_READ_BYTES, MAX_IMAGE_DOWNSAMPLE_INPUT_BYTES, NO_DOWNSAMPLER_IMAGE_CAP_HINT, resolveAutoDownsampler, MAX_READ_OUTPUT_CHARS, readCyberReminderText, FILE_PATH_PARAMS, countLines, seededFileUnchangedReminder, enoentMessage, } from "./fs-shared.js";
|
|
9
9
|
import { readPdfFile, pdfResultToToolReturn } from "./fs-pdf.js";
|
|
10
10
|
import { openSystemReminder } from "../../core/reminder-mint.js";
|
|
11
|
-
|
|
11
|
+
import { discloseReminderShaped } from "../../core/reminder-disclosure.js";
|
|
12
|
+
export function createReadFileTool(env, state, rootCanonical, cwdRef, additionalRoots, imageDownsampler, pdfCapabilities, bgOutputReadExemption, readCyberReminder, readDeny, readFace, reminderMark, reminderDisclosureCounts) {
|
|
12
13
|
const cyberReminder = readCyberReminder === false ? "" : readCyberReminderText(reminderMark);
|
|
14
|
+
const reminderDisclosureWindows = new Map();
|
|
15
|
+
const pdfReminderDisclosure = (key) => ({
|
|
16
|
+
mark: reminderMark,
|
|
17
|
+
windows: reminderDisclosureWindows,
|
|
18
|
+
key,
|
|
19
|
+
counts: reminderDisclosureCounts,
|
|
20
|
+
});
|
|
13
21
|
const pathBoundLine = readFace === "open"
|
|
14
22
|
? "- `file_path` may be relative (resolved against the tracked working directory) or absolute. Reads are not confined to the workspace roots; a small sensitive-path deny list applies.\n"
|
|
15
23
|
: "- `file_path` may be relative (resolved against the tracked working directory) or absolute (within the configured roots).\n";
|
|
@@ -125,7 +133,7 @@ export function createReadFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
125
133
|
};
|
|
126
134
|
}
|
|
127
135
|
if (r.key.toLowerCase().endsWith(".pdf")) {
|
|
128
|
-
return pdfResultToToolReturn(await readPdfFile(env, path, r.key, pages, ctx.signal, imageDownsampler, cwdRef?.current ?? rootCanonical, undefined, pdfCapabilities, readDeny));
|
|
136
|
+
return pdfResultToToolReturn(await readPdfFile(env, path, r.key, pages, ctx.signal, imageDownsampler, cwdRef?.current ?? rootCanonical, undefined, pdfCapabilities, readDeny, pdfReminderDisclosure(r.key)));
|
|
129
137
|
}
|
|
130
138
|
const binaryExt = hasBinaryExtension(r.key);
|
|
131
139
|
const binaryExtRefusal = () => errorResult(`Error (Read): "${path}" appears to be a binary file (by extension); this tool reads UTF-8 text only.`);
|
|
@@ -170,7 +178,7 @@ export function createReadFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
170
178
|
return errorResult(`Error (Read): "${path}" is too large to read in full (${readSize} bytes > ${MAX_READ_BYTES}-byte cap); pass an explicit offset/limit to read a slice, or use grep to search it instead.`);
|
|
171
179
|
}
|
|
172
180
|
if (pdfMagicMatches(readBin.value)) {
|
|
173
|
-
return pdfResultToToolReturn(await readPdfFile(env, path, r.key, pages, ctx.signal, imageDownsampler, cwdRef?.current ?? rootCanonical, readBin.value, pdfCapabilities, readDeny));
|
|
181
|
+
return pdfResultToToolReturn(await readPdfFile(env, path, r.key, pages, ctx.signal, imageDownsampler, cwdRef?.current ?? rootCanonical, readBin.value, pdfCapabilities, readDeny, pdfReminderDisclosure(r.key)));
|
|
174
182
|
}
|
|
175
183
|
const magicFormat = binaryMagicFormat(readBin.value);
|
|
176
184
|
if (magicFormat !== undefined) {
|
|
@@ -225,8 +233,20 @@ export function createReadFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
225
233
|
}
|
|
226
234
|
state.set(r.key, { hash, totalLines: countLines(content), truncated: false, view: { start: 1, end: total }, lastReadAt: Date.now() });
|
|
227
235
|
const bodyBlocks = rendered.blocks.length > 0 ? rendered.blocks : [{ type: "text", text: "[notebook has 0 cells]" }];
|
|
236
|
+
const nbDisclosure = discloseReminderShaped({
|
|
237
|
+
segments: rendered.blocks.filter((b) => b.type === "text").map((b) => b.text),
|
|
238
|
+
mark: reminderMark,
|
|
239
|
+
outlet: "notebook",
|
|
240
|
+
defuseExactMark: false,
|
|
241
|
+
throttle: { key: r.key, windows: reminderDisclosureWindows },
|
|
242
|
+
counts: reminderDisclosureCounts,
|
|
243
|
+
});
|
|
228
244
|
return {
|
|
229
|
-
content:
|
|
245
|
+
content: [
|
|
246
|
+
...bodyBlocks,
|
|
247
|
+
...(cyberReminder ? [{ type: "text", text: cyberReminder }] : []),
|
|
248
|
+
...(nbDisclosure.trailer !== undefined ? [{ type: "text", text: nbDisclosure.trailer }] : []),
|
|
249
|
+
],
|
|
230
250
|
details: { type: "notebook", file: { filePath: path, cells: parsed.cells.map(stripNotebookImageData) } },
|
|
231
251
|
};
|
|
232
252
|
}
|
|
@@ -291,8 +311,16 @@ export function createReadFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
291
311
|
if (total === 0)
|
|
292
312
|
return `${openSystemReminder(reminderMark)}Warning: the file exists but the contents are empty.</system-reminder>`;
|
|
293
313
|
const header = pageMarker ?? (truncated ? `[${path}: lines ${start}-${end} of ${total}${end < total ? " — use offset to see more" : ""}]\n` : "");
|
|
314
|
+
const disclosure = discloseReminderShaped({
|
|
315
|
+
segments: [body],
|
|
316
|
+
mark: reminderMark,
|
|
317
|
+
outlet: "read",
|
|
318
|
+
defuseExactMark: false,
|
|
319
|
+
throttle: { key: r.key, windows: reminderDisclosureWindows },
|
|
320
|
+
counts: reminderDisclosureCounts,
|
|
321
|
+
});
|
|
294
322
|
return {
|
|
295
|
-
content: `${nbFallbackPrefix}${header}${body}${cyberReminder}`,
|
|
323
|
+
content: `${nbFallbackPrefix}${header}${body}${cyberReminder}${disclosure.trailer !== undefined ? `\n${disclosure.trailer}` : ""}`,
|
|
296
324
|
details: {
|
|
297
325
|
type: "text",
|
|
298
326
|
file: {
|
|
@@ -140,8 +140,12 @@ export declare const MAX_READ_OUTPUT_CHARS = 100000;
|
|
|
140
140
|
* COVERAGE, stated because the switch is easy to over-read: the reminder rides the plain-text read and
|
|
141
141
|
* the notebook projection. PDF text extraction returns through its own result builder and has never
|
|
142
142
|
* carried it, so extracted PDF text reaches the model without this mitigation whatever the switch says.
|
|
143
|
-
* That gap predates the switch
|
|
144
|
-
*
|
|
143
|
+
* That gap predates the switch; its IMPERSONATION half is now closed — design/319 (B ticket) gave the
|
|
144
|
+
* PDF text legs the same detect-and-disclose trailer as this lane (reminder-shaped bytes in extracted
|
|
145
|
+
* PDF text are disclosed at the outlet, fs-pdf.ts), and their bodies were already fenced. The CYBER
|
|
146
|
+
* half (this malware-alertness sentence) still does not ride PDF text and stays an open item rather
|
|
147
|
+
* than closed silently — widening it to a third surface is a change to what every PDF read costs,
|
|
148
|
+
* not a wiring fix.
|
|
145
149
|
*
|
|
146
150
|
* design/319 (A ticket): a FUNCTION over the session mark (formerly the `READ_CYBER_REMINDER`
|
|
147
151
|
* constant) — the open tag is rendered by the mint home so it carries the run's provenance mark;
|
package/dist/tools/fs/index.d.ts
CHANGED
|
@@ -161,6 +161,13 @@ export interface HandsToolkitOptions {
|
|
|
161
161
|
* the honest form). The tag is rendered by the mint home (core/reminder-mint.ts) — the content
|
|
162
162
|
* BODY next to it is byte-untouched. */
|
|
163
163
|
reminderMark?: string;
|
|
164
|
+
/** design/319 (B ticket) — the per-run trailer/defuse trigger counters (observation seat, G9②):
|
|
165
|
+
* the Read text/notebook/PDF-text disclosure trailers bump `read.*` / `notebook.*` / `pdf.*`
|
|
166
|
+
* keys here (appended / marked / bare_throttled). Threaded by prepare-task, which folds the
|
|
167
|
+
* non-zero result into `TaskResult.stats.mechanisms.reminderDisclosures`; a library-direct
|
|
168
|
+
* mount may pass its own object or omit it (counting off — and the whole disclosure pipeline
|
|
169
|
+
* is off anyway when `reminderMark` is absent). */
|
|
170
|
+
reminderDisclosureCounts?: import("../../core/reminder-disclosure.js").ReminderDisclosureCounts;
|
|
164
171
|
}
|
|
165
172
|
/**
|
|
166
173
|
* Build the per-task hand tool band over an injected env + fresh per-task read state (design/44 §11 A).
|
package/dist/tools/fs/index.js
CHANGED
|
@@ -46,7 +46,7 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
|
|
|
46
46
|
onDeploymentClamp: () => deliverEngineNotice(opts.onNotice, deploymentReadFaceClampNotice()),
|
|
47
47
|
});
|
|
48
48
|
const tools = [
|
|
49
|
-
createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption, opts.readCyberReminder, readDeny, readFace, opts.reminderMark),
|
|
49
|
+
createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption, opts.readCyberReminder, readDeny, readFace, opts.reminderMark, opts.reminderDisclosureCounts),
|
|
50
50
|
];
|
|
51
51
|
if (!readOnly) {
|
|
52
52
|
tools.push(createEditFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite), createWriteFileTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite), createNotebookEditTool(env, readFileState, rootCanonical, cwdRef, additionalRoots, opts.beforeWrite));
|
package/dist/tools/web.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { defineTool, errorResult } from "../core/tools.js";
|
|
3
3
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
4
|
+
import { discloseReminderShaped } from "../core/reminder-disclosure.js";
|
|
4
5
|
import { redactSecrets } from "../core/untrusted-egress.js";
|
|
5
6
|
import { isPrivateHost } from "../core/runner/image.js";
|
|
6
7
|
import { binaryMagicFormat } from "./fs/safety.js";
|
|
@@ -638,7 +639,17 @@ export function webFetchToolSpec(config = {}) {
|
|
|
638
639
|
const partialNote = bodyCut
|
|
639
640
|
? `\n\n[WebFetch: ${cutPhrase} — the content above is PARTIAL: ${bodyBytes?.length ?? 0} bytes were received before the cutoff and the tail is missing. Treat absent information as unfetched, not absent from the source.]`
|
|
640
641
|
: "";
|
|
641
|
-
|
|
642
|
+
let modelText = withNote + truncationNote + partialNote + sourceEcho;
|
|
643
|
+
{
|
|
644
|
+
const d = discloseReminderShaped({
|
|
645
|
+
segments: [modelText],
|
|
646
|
+
mark: ctx.reminderMark,
|
|
647
|
+
outlet: "webFetch",
|
|
648
|
+
defuseExactMark: true,
|
|
649
|
+
...(ctx.reminderDisclosureCounts !== undefined ? { counts: ctx.reminderDisclosureCounts } : {}),
|
|
650
|
+
});
|
|
651
|
+
modelText = d.trailer !== undefined ? `${d.segments[0]}\n\n${d.trailer}` : d.segments[0];
|
|
652
|
+
}
|
|
642
653
|
const RESULT_PREVIEW_CHARS = 8_000;
|
|
643
654
|
return {
|
|
644
655
|
content: modelText,
|
|
@@ -947,7 +958,15 @@ export function createWebSearchTool(config) {
|
|
|
947
958
|
const tailReminder = shown.length > 0
|
|
948
959
|
? "REMINDER: You MUST include the sources above in your response to the user using markdown hyperlinks."
|
|
949
960
|
: "REMINDER: this search returned ZERO usable sources — there is nothing above to cite. Do not fabricate sources, URLs or citations; tell the user the search returned no usable results (the notes above say why, when there is a why) and answer from what you already know, or try a different query.";
|
|
950
|
-
const
|
|
961
|
+
const searchDisclosure = discloseReminderShaped({
|
|
962
|
+
segments: [fenced],
|
|
963
|
+
mark: ctx.reminderMark,
|
|
964
|
+
outlet: "webSearch",
|
|
965
|
+
defuseExactMark: true,
|
|
966
|
+
...(ctx.reminderDisclosureCounts !== undefined ? { counts: ctx.reminderDisclosureCounts } : {}),
|
|
967
|
+
});
|
|
968
|
+
const searchDisclosureNote = searchDisclosure.trailer !== undefined ? `\n\n${searchDisclosure.trailer}` : "";
|
|
969
|
+
const modelText = `${searchDisclosure.segments[0]}${dropNote}${domainNote}${maxResultsNote}${schemeNote}${clipNote}${searchDisclosureNote}\n\n${tailReminder}`;
|
|
951
970
|
return {
|
|
952
971
|
content: modelText,
|
|
953
972
|
details: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/core",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.48.0",
|
|
4
4
|
"description": "Stateless, task-oriented AI agent core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "BUSL-1.1",
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
"gate:nuia": "node scripts/verify-nuia-baseline.mjs",
|
|
67
67
|
"gate:field-liveness": "node scripts/verify-field-liveness.mjs",
|
|
68
68
|
"gate:error-surface": "node scripts/verify-error-surface.mjs",
|
|
69
|
-
"gate:criteria": "node scripts/criteria-lint.mjs"
|
|
69
|
+
"gate:criteria": "node scripts/criteria-lint.mjs",
|
|
70
|
+
"gate:reminder-literal": "node scripts/verify-reminder-literal.mjs"
|
|
70
71
|
},
|
|
71
72
|
"dependencies": {
|
|
72
73
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_comment": "design/87 L3 — frozen public export surface of src/index.ts (name -> kind). DO NOT edit by hand to silence a red test. A removed/changed entry = a SemVer-BREAKING change; bump MAJOR and update this fixture in the SAME commit (design/87 §4.2 / §5.2). Regenerate via REGEN in test/export-surface.test.ts.",
|
|
3
|
-
"count":
|
|
3
|
+
"count": 1639,
|
|
4
4
|
"exports": {
|
|
5
5
|
"A2ATaskState": "type",
|
|
6
6
|
"A2ATaskStateReversal": "type",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"AdoptionSource": "type",
|
|
48
48
|
"AdoptionStatus": "type",
|
|
49
49
|
"AffectedDeploymentConfig": "interface",
|
|
50
|
+
"AgentContinuationReceipt": "interface",
|
|
50
51
|
"AgentDefinition": "interface",
|
|
51
52
|
"AgentDisplayStatus": "type",
|
|
52
53
|
"AgentPollDetailsInput": "interface",
|
|
@@ -229,6 +230,8 @@
|
|
|
229
230
|
"DEFAULT_TIER_ORDER": "variable",
|
|
230
231
|
"DEFAULT_TOOL_RESULT_THRESHOLD_CHARS": "variable",
|
|
231
232
|
"DEGRADED_DIAGNOSTIC_TYPE": "variable",
|
|
233
|
+
"DELEGATION_MAX_CONCURRENT_DEFAULT": "variable",
|
|
234
|
+
"DELEGATION_MAX_PER_SESSION_DEFAULT": "variable",
|
|
232
235
|
"DESIGN_REVIEW_PROMPTS": "variable",
|
|
233
236
|
"DISCUSSION_SCRIPT": "variable",
|
|
234
237
|
"DISCUSSION_WORKFLOW_NAME": "variable",
|
|
@@ -547,6 +550,8 @@
|
|
|
547
550
|
"ORG_ADMISSION_CHECKPOINT_VERSION": "variable",
|
|
548
551
|
"ORG_RULE_DECISION_REASON": "variable",
|
|
549
552
|
"ORG_UNAVAILABLE_DECISION_REASON": "variable",
|
|
553
|
+
"ORPHAN_ADOPT_MAX_DEFAULT": "variable",
|
|
554
|
+
"ORPHAN_ADOPT_WINDOW_MS_DEFAULT": "variable",
|
|
550
555
|
"OUTPUT_EFFICIENCY": "variable",
|
|
551
556
|
"OnAsk": "type",
|
|
552
557
|
"OnElicit": "type",
|
|
@@ -636,6 +641,7 @@
|
|
|
636
641
|
"PersistedRuleMatch": "type",
|
|
637
642
|
"PersistedRuleTool": "type",
|
|
638
643
|
"PersistedRuleUnreadable": "interface",
|
|
644
|
+
"PlacedSessionRow": "type",
|
|
639
645
|
"PlatformLimitReason": "type",
|
|
640
646
|
"PostCompactContext": "interface",
|
|
641
647
|
"PostToolBatchCall": "interface",
|
|
@@ -748,6 +754,7 @@
|
|
|
748
754
|
"ReportedFinding": "interface",
|
|
749
755
|
"ResolveExpectation": "interface",
|
|
750
756
|
"ResolvedAsk": "type",
|
|
757
|
+
"ResolvedDelegationEntryCaps": "interface",
|
|
751
758
|
"ResolvedOutcome": "interface",
|
|
752
759
|
"ResolvedReasoning": "interface",
|
|
753
760
|
"ResolvedRole": "interface",
|
|
@@ -831,6 +838,7 @@
|
|
|
831
838
|
"STUB_ARCHIVED_LINE": "variable",
|
|
832
839
|
"SUBAGENT_PROMPT": "variable",
|
|
833
840
|
"SUBAGENT_SYSTEM_NOTE": "variable",
|
|
841
|
+
"SUBAGENT_TRANSCRIPT_RETENTION_DAYS_DEFAULT": "variable",
|
|
834
842
|
"SUMMARIZE_TOOL_RESULTS": "variable",
|
|
835
843
|
"SUPERVISOR_PROMPT": "variable",
|
|
836
844
|
"SafetyAxis": "interface",
|
|
@@ -869,6 +877,8 @@
|
|
|
869
877
|
"SessionError": "class",
|
|
870
878
|
"SessionMetadata": "interface",
|
|
871
879
|
"SessionPermissionRules": "interface",
|
|
880
|
+
"SessionPlacement": "interface",
|
|
881
|
+
"SessionPlacementRecord": "interface",
|
|
872
882
|
"SessionPolicyError": "class",
|
|
873
883
|
"SessionPolicyStore": "interface",
|
|
874
884
|
"SessionRepo": "interface",
|
|
@@ -930,6 +940,7 @@
|
|
|
930
940
|
"SubagentSteerHandle": "interface",
|
|
931
941
|
"SubagentStep": "interface",
|
|
932
942
|
"SubagentToolOptions": "interface",
|
|
943
|
+
"SubagentTranscriptTier": "type",
|
|
933
944
|
"SummarizableFinding": "interface",
|
|
934
945
|
"SyncMemoryScopeOptions": "interface",
|
|
935
946
|
"SyntheticContinuationReason": "type",
|
|
@@ -1206,6 +1217,7 @@
|
|
|
1206
1217
|
"constraintChainEntryOf": "function",
|
|
1207
1218
|
"cosineDistance": "function",
|
|
1208
1219
|
"countElicitOptIns": "function",
|
|
1220
|
+
"createAgentContinuationVerb": "function",
|
|
1209
1221
|
"createAgentTranscriptTool": "function",
|
|
1210
1222
|
"createAllowDenyPolicy": "function",
|
|
1211
1223
|
"createAnthropicBrain": "function",
|
|
@@ -1499,6 +1511,7 @@
|
|
|
1499
1511
|
"resolveComplianceDenies": "function",
|
|
1500
1512
|
"resolveDataRoot": "function",
|
|
1501
1513
|
"resolveDeclaredDurability": "function",
|
|
1514
|
+
"resolveDelegationEntryCaps": "function",
|
|
1502
1515
|
"resolveEffectiveConfig": "function",
|
|
1503
1516
|
"resolveEffort": "function",
|
|
1504
1517
|
"resolveFrozenPaths": "function",
|
|
@@ -1513,6 +1526,7 @@
|
|
|
1513
1526
|
"resolveReadFace": "function",
|
|
1514
1527
|
"resolveReasoning": "function",
|
|
1515
1528
|
"resolveReasoningProfile": "function",
|
|
1529
|
+
"resolveSubagentTranscriptTier": "function",
|
|
1516
1530
|
"resolveTaskLimits": "function",
|
|
1517
1531
|
"resolveTaskModel": "function",
|
|
1518
1532
|
"resolveUsageWindows": "function",
|