@sema-agent/core 7.41.0 → 7.42.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/dist/agents/subagent.js +2 -0
  3. package/dist/core/agent-definition.d.ts +1 -1
  4. package/dist/core/allow-entry.d.ts +241 -0
  5. package/dist/core/allow-entry.js +340 -0
  6. package/dist/core/allow-rule-shadow.d.ts +18 -0
  7. package/dist/core/allow-rule-shadow.js +1 -0
  8. package/dist/core/ask-origin.d.ts +5 -5
  9. package/dist/core/ask-origin.js +1 -1
  10. package/dist/core/auto-mode.d.ts +3 -2
  11. package/dist/core/checkpoint-store.d.ts +11 -19
  12. package/dist/core/checkpoint-store.js +1 -1
  13. package/dist/core/decision-receipt.d.ts +4 -2
  14. package/dist/core/decision-receipt.js +1 -1
  15. package/dist/core/engine-notice.d.ts +8 -5
  16. package/dist/core/gate-fold.js +1 -1
  17. package/dist/core/gate-lanes.d.ts +1 -49
  18. package/dist/core/gate-lanes.js +85 -223
  19. package/dist/core/governance-codes.d.ts +3 -3
  20. package/dist/core/governance-codes.js +3 -3
  21. package/dist/core/hooks.d.ts +13 -10
  22. package/dist/core/hooks.js +5 -3
  23. package/dist/core/mode-policy.d.ts +3 -3
  24. package/dist/core/mode-policy.js +2 -2
  25. package/dist/core/permission-rules.d.ts +24 -23
  26. package/dist/core/permission-rules.js +37 -33
  27. package/dist/core/reminder-disclosure.d.ts +53 -2
  28. package/dist/core/reminder-disclosure.js +49 -36
  29. package/dist/core/rule-lane-reads.d.ts +84 -0
  30. package/dist/core/rule-lane-reads.js +164 -0
  31. package/dist/core/runner/contracts.d.ts +4 -2
  32. package/dist/core/runner/gate-exit.d.ts +3 -3
  33. package/dist/core/runner/image.js +4 -3
  34. package/dist/core/runner/inline-image.d.ts +14 -0
  35. package/dist/core/runner/inline-image.js +26 -0
  36. package/dist/core/runner/permission-rule-lanes.d.ts +3 -1
  37. package/dist/core/runner/permission-rule-lanes.js +1 -1
  38. package/dist/core/runner/prepare-ask-lane.d.ts +0 -19
  39. package/dist/core/runner/prepare-ask-lane.js +2 -2
  40. package/dist/core/runner/prepare-inherited-gate.d.ts +2 -0
  41. package/dist/core/runner/prepare-inherited-gate.js +1 -0
  42. package/dist/core/runner/prepare-park-ask.js +3 -3
  43. package/dist/core/runner/prepare-policy-chain.d.ts +12 -0
  44. package/dist/core/runner/prepare-policy-chain.js +10 -9
  45. package/dist/core/runner/prepare-project-context.d.ts +13 -3
  46. package/dist/core/runner/prepare-project-context.js +5 -4
  47. package/dist/core/runner/prepare-task.js +8 -5
  48. package/dist/core/runner/prepare-turn-wiring.d.ts +16 -3
  49. package/dist/core/runner/prepare-turn-wiring.js +19 -18
  50. package/dist/core/runner/run-leg.js +1 -1
  51. package/dist/core/runner/runtask.js +2 -1
  52. package/dist/core/runner-deps.d.ts +11 -0
  53. package/dist/core/skill-grant.d.ts +8 -47
  54. package/dist/core/skill-grant.js +15 -136
  55. package/dist/core/synthetic-read-verdict.d.ts +108 -0
  56. package/dist/core/synthetic-read-verdict.js +111 -0
  57. package/dist/core/task-spec.d.ts +18 -0
  58. package/dist/core/tool-name-readings.d.ts +1 -1
  59. package/dist/core/tool-name-readings.js +1 -1
  60. package/dist/core/tool-policy.d.ts +25 -24
  61. package/dist/core/tool-policy.js +26 -17
  62. package/dist/core/tool-spec.d.ts +7 -0
  63. package/dist/core/trace.d.ts +16 -0
  64. package/dist/index.d.ts +5 -1
  65. package/dist/index.js +4 -0
  66. package/dist/internal/harness-types.d.ts +1 -1
  67. package/dist/tools/fs/dir-entries.d.ts +69 -0
  68. package/dist/tools/fs/dir-entries.js +60 -0
  69. package/dist/tools/fs/fs-pdf.d.ts +7 -11
  70. package/dist/tools/fs/fs-pdf.js +5 -14
  71. package/dist/tools/fs/fs-read.d.ts +173 -2
  72. package/dist/tools/fs/fs-read.js +399 -286
  73. package/dist/tools/fs/image-admission.d.ts +51 -0
  74. package/dist/tools/fs/image-admission.js +36 -0
  75. package/dist/tools/fs/search.js +3 -7
  76. package/package.json +1 -1
  77. package/test/export-surface.snapshot.json +29 -1
  78. package/wire-changes/core@7.41.1.json +159 -0
  79. package/wire-changes/core@7.42.0.json +1107 -0
@@ -0,0 +1,60 @@
1
+ import { homeViewsOf } from "./read-deny-home.js";
2
+ export const DIRECTORY_LISTING_MAX_ENTRIES = 1000;
3
+ export function createEntryDenyJudge(env, deny, signal) {
4
+ const views = homeViewsOf(env, deny, signal);
5
+ return async (path, leaf) => {
6
+ const hit = deny.matchPath(path);
7
+ if (hit)
8
+ return hit;
9
+ const view = await views.of(path, leaf !== undefined ? { leaf } : undefined);
10
+ return view !== undefined ? deny.matchPath(view) : null;
11
+ };
12
+ }
13
+ export async function enumerateDirectory(env, dir, judge, signal) {
14
+ const listing = await env.listDir(dir, signal);
15
+ if (!listing.ok)
16
+ return listing;
17
+ const infos = [...listing.value].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
18
+ const kept = [];
19
+ let omitted = 0;
20
+ const omittedPatterns = [];
21
+ let incomplete = false;
22
+ for (const info of infos) {
23
+ if (signal?.aborted) {
24
+ incomplete = true;
25
+ break;
26
+ }
27
+ const hit = judge === undefined ? null : await judge(info.path, info.kind === "directory" ? undefined : "file");
28
+ if (hit) {
29
+ omitted++;
30
+ if (!omittedPatterns.includes(hit.pattern))
31
+ omittedPatterns.push(hit.pattern);
32
+ continue;
33
+ }
34
+ kept.push({ name: info.name, kind: info.kind });
35
+ }
36
+ return {
37
+ ok: true,
38
+ value: {
39
+ entries: kept.slice(0, DIRECTORY_LISTING_MAX_ENTRIES),
40
+ more: Math.max(0, kept.length - DIRECTORY_LISTING_MAX_ENTRIES),
41
+ omitted,
42
+ omittedPatterns,
43
+ incomplete,
44
+ },
45
+ };
46
+ }
47
+ const LABEL_UNSAFE = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
48
+ export function quoteDataLabel(label) {
49
+ let out = "";
50
+ for (const ch of JSON.stringify(label)) {
51
+ out += LABEL_UNSAFE.test(ch) ? `\\u${ch.codePointAt(0).toString(16).padStart(4, "0")}` : ch;
52
+ }
53
+ return out;
54
+ }
55
+ export function renderDirectoryListing(l) {
56
+ const lines = l.entries.map((e) => `${LABEL_UNSAFE.test(e.name) ? quoteDataLabel(e.name) : e.name}${e.kind === "directory" ? "/" : ""}`);
57
+ if (l.more > 0)
58
+ lines.push(`… and ${l.more} more entries`);
59
+ return lines.join("\n");
60
+ }
@@ -1,18 +1,18 @@
1
- import { errorResult } from "../../core/tools.js";
2
1
  import type { DocumentContent, ImageContent, TextContent } from "../../internal/llm.js";
3
2
  import type { ExecutionEnv } from "../../internal/harness-types.js";
4
3
  import { type PdfModelCapabilities } from "./pdf.js";
5
4
  import { type ReadImageDownsamplerOption } from "./fs-shared.js";
6
- import { type ReminderDisclosureCounts } from "../../core/reminder-disclosure.js";
7
- /** 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. */
5
+ import { type ReminderDisclosureEffects } from "../../core/reminder-disclosure.js";
6
+ /** The reader's disclosure state, threaded into the PDF TEXT legs (the two `pdftotext` extraction arms —
7
+ * the only PDF returns with a model-facing text projection of the document; the native document block and
8
+ * rendered page images have no text to scan). Shares the reader's throttle windows so a PDF and its text
9
+ * read dedup on the same file key. The windows are READ here and never advanced: each planned
10
+ * disclosure's effects are pushed onto `effects`, and the reader commits them with the delivery. */
11
11
  export interface PdfReminderDisclosure {
12
12
  mark: string | undefined;
13
13
  windows: Map<string, number>;
14
14
  key: string;
15
- counts?: ReminderDisclosureCounts;
15
+ effects: ReminderDisclosureEffects[];
16
16
  }
17
17
  export { pdfModelCapabilitiesOf, type PdfModelCapabilities } from "./pdf.js";
18
18
  /** What the Read tool's PDF pipeline returns (document block, or rendered page images, or an error string). */
@@ -45,7 +45,3 @@ type ReadPdfReturn = string | {
45
45
  * `caps` absent ⇒ fully capable (native document block; the brain placeholder still guards).
46
46
  */
47
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>;
48
- /** readPdfFile's own return type stays `ReadPdfReturn` (its INTERNAL string-means-error dispatch
49
- * contract, shared with pdfPagesToImageBlocks) — the isError flag is applied once, here, at the tool's
50
- * actual execute() boundary, not inside the helper. */
51
- export declare function pdfResultToToolReturn(r: ReadPdfReturn): ReturnType<typeof errorResult> | Exclude<ReadPdfReturn, string>;
@@ -1,21 +1,15 @@
1
- import { errorResult } from "../../core/tools.js";
2
1
  import { delimitUntrusted } from "../../core/untrusted-text.js";
3
2
  import { MCP_IMAGE_MAX_BASE64 } from "../../core/mcp.js";
4
3
  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
4
  import { resolveAutoDownsampler, enoentMessage, MAX_READ_BYTES } from "./fs-shared.js";
6
- import { discloseReminderShaped } from "../../core/reminder-disclosure.js";
5
+ import { planReminderDisclosure, renderReminderDisclosureTrailer } from "../../core/reminder-disclosure.js";
7
6
  function pdfDisclosureTrailer(body, d) {
8
7
  if (d === undefined)
9
8
  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}` : "";
9
+ const plan = planReminderDisclosure({ segments: [body], mark: d.mark, outlet: "pdf", defuseExactMark: false, throttle: { key: d.key, windows: d.windows } });
10
+ d.effects.push(plan.effects);
11
+ const trailer = renderReminderDisclosureTrailer(plan.verdict, d.mark);
12
+ return trailer !== undefined ? `\n${trailer}` : "";
19
13
  }
20
14
  export { pdfModelCapabilitiesOf } from "./pdf.js";
21
15
  async function pdfPagesToImageBlocks(pages, path, downsamplerOpt) {
@@ -245,6 +239,3 @@ export async function readPdfFile(env, path, key, pages, signal, downsamplerOpt,
245
239
  },
246
240
  };
247
241
  }
248
- export function pdfResultToToolReturn(r) {
249
- return typeof r === "string" ? errorResult(r) : r;
250
- }
@@ -1,8 +1,179 @@
1
+ import type { DocumentContent, ImageContent, TextContent } from "../../internal/llm.js";
1
2
  import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
2
- import { type ReadFileState, type RootsSeat } from "./safety.js";
3
+ import { errorResult } from "../../core/tools.js";
4
+ import type { ToolReturn } from "../../core/tool-spec.js";
5
+ import { type ReadEntry, type ReadFileState, type RootsSeat } from "./safety.js";
6
+ import { type ImageDimensions } from "../../core/mcp.js";
3
7
  import { type PdfModelCapabilities } from "./pdf.js";
4
8
  import { type ReadImageDownsamplerOption, type CwdRef } from "./fs-shared.js";
5
- import { type ReminderDisclosureCounts } from "../../core/reminder-disclosure.js";
9
+ import { type ReminderDisclosureCounts, type ReminderDisclosureEffects, type ReminderDisclosureVerdict } from "../../core/reminder-disclosure.js";
10
+ /**
11
+ * The Read tool, and the ONE read of a file for the model it is built on, split into three steps so every
12
+ * carrier shares it:
13
+ *
14
+ * readForModel(args) → a value: the external data blocks, the engine's STRUCTURED notes about the read,
15
+ * the read-state observation, the deferred effects, an outcome word;
16
+ * commit(result) → the side effects, applied when the carrier actually delivers the result (the
17
+ * read-state entry, the disclosure throttle windows and counters);
18
+ * render → per carrier. {@link renderReadToolResult} is the Read tool's: it reproduces the
19
+ * tool result byte for byte (the Read pins are the proof); a carrier that delivers
20
+ * a file somewhere else renders the same notes in its own words.
21
+ *
22
+ * `readForModel` is side-effect free toward the run: it reads the filesystem (and, on the PDF lane, runs
23
+ * the poppler readers), it READS the read state and the throttle windows, and it writes neither.
24
+ *
25
+ * What is DATA and what is a NOTE. `data` holds what the file itself supplies, in order — the
26
+ * line-numbered body, the notebook cell blocks, the image block. Everything the engine says ABOUT the
27
+ * read (empty file, offset past the end, already in context, unchanged since the last read, the range
28
+ * header, the partial-view marker, the corrupt-notebook prefix, the 0-cell placeholder, the resize
29
+ * coordinates, the disclosure verdict) is a structured {@link ReadNote} — never a pre-rendered string, so
30
+ * no carrier inherits the tool carrier's wording (a name inside an engine sentence is the tool
31
+ * carrier's own byte, not a rule for other carriers). The PDF lane is the one exception: its blocks are
32
+ * the Read tool's PDF projection as-is (engine headers interleaved with the fenced text layer) — a
33
+ * carrier other than the Read tool does not present them as file data.
34
+ *
35
+ * A refusal is the Read tool's own error envelope (`refusal`), minted where it is decided, with
36
+ * `outcome: "not_attached"`.
37
+ */
38
+ /** What happened to the file's contents. `partial` = a default read the token cap cut (the partial-view
39
+ * marker); `unchanged` = the dedup answers (same window and hash, or a seeded file already in context);
40
+ * `not_attached` = nothing of the contents was served (a refusal, an offset past the end, a PDF whose
41
+ * degradation chain bottomed out). Exported to carriers by the seat that needs it, not from the package. */
42
+ export declare const READ_OUTCOMES: readonly ["attached", "partial", "unchanged", "not_attached"];
43
+ export type ReadOutcome = (typeof READ_OUTCOMES)[number];
44
+ /** The lane the read took. Absent on a refusal raised before the lane was known. A corrupt notebook served
45
+ * as raw text is `text` (with the `notebook_fallback` note). */
46
+ export type ReadKind = "text" | "notebook" | "image" | "pdf";
47
+ /** The engine's statements about one read, as data. The Read carrier's rendering is
48
+ * {@link renderReadNoteForTool}; the frame carrier renders the same members its own way. */
49
+ export type ReadNote = {
50
+ kind: "empty_file";
51
+ } | {
52
+ kind: "past_eof";
53
+ offset: number;
54
+ totalLines: number;
55
+ }
56
+ /** A default read of a startup-seeded file that is unchanged on disk. `key` is the canonical path. */
57
+ | {
58
+ kind: "already_in_context";
59
+ key: string;
60
+ }
61
+ /** The same window of an unchanged file was already served. `path` is the caller's spelling. */
62
+ | {
63
+ kind: "unchanged";
64
+ path: string;
65
+ start: number;
66
+ end: number;
67
+ total: number;
68
+ }
69
+ /** A served window that is not the whole file (an explicit slice), no partial-view marker. */
70
+ | {
71
+ kind: "range";
72
+ path: string;
73
+ start: number;
74
+ end: number;
75
+ total: number;
76
+ }
77
+ /** A default read the token cap cut by lines. */
78
+ | {
79
+ kind: "partial_page";
80
+ end: number;
81
+ total: number;
82
+ approxTokens: number;
83
+ }
84
+ /** A default read whose first line alone overflows the cap, served as a character prefix. */
85
+ | {
86
+ kind: "partial_chars";
87
+ cut: number;
88
+ totalChars: number;
89
+ approxTokens: number;
90
+ }
91
+ /** A `.ipynb` that does not parse, served as raw text. */
92
+ | {
93
+ kind: "notebook_fallback";
94
+ }
95
+ /** A parsed notebook with no cells. */
96
+ | {
97
+ kind: "notebook_no_cells";
98
+ }
99
+ /** The image was downsampled; the coordinates map back to the original. */
100
+ | {
101
+ kind: "image_resized";
102
+ dimensions: ImageDimensions;
103
+ }
104
+ /** External bytes carried reminder- or envelope-shaped text. */
105
+ | {
106
+ kind: "disclosure";
107
+ verdict: ReminderDisclosureVerdict;
108
+ };
109
+ /** The read-state entry a delivery records, minus the delivery's own facts (when, which transmission). */
110
+ export interface ReadObservation {
111
+ key: string;
112
+ entry: Pick<ReadEntry, "hash" | "totalLines" | "truncated" | "isPartialView" | "view">;
113
+ }
114
+ /** The Read tool's error envelope for a refusal. */
115
+ export type ReadRefusal = ReturnType<typeof errorResult>;
116
+ export interface ReadForModelResult {
117
+ outcome: ReadOutcome;
118
+ kind?: ReadKind;
119
+ data: Array<TextContent | ImageContent | DocumentContent>;
120
+ notes: ReadNote[];
121
+ /** The host face (the Read tool's `details`), shared by every carrier. */
122
+ details?: Record<string, unknown>;
123
+ /** The Read tool's refusal (its error envelope: the sentence and its structured details) — present exactly on a
124
+ * refusal. */
125
+ refusal?: ReadRefusal;
126
+ /** Present when a delivery records read state. */
127
+ observation?: ReadObservation;
128
+ effects: {
129
+ disclosure: ReminderDisclosureEffects[];
130
+ };
131
+ }
132
+ /** The facts of the read the caller supplies (the tool context's subset). */
133
+ export interface ReadForModelContext {
134
+ signal?: AbortSignal;
135
+ toolCallId?: string;
136
+ taskId?: string;
137
+ principal?: string;
138
+ }
139
+ export interface ModelFileReaderOptions {
140
+ env: ExecutionEnv;
141
+ state: ReadFileState;
142
+ rootCanonical: string;
143
+ cwdRef?: CwdRef;
144
+ additionalRoots?: RootsSeat;
145
+ imageDownsampler?: ReadImageDownsamplerOption;
146
+ pdfCapabilities?: PdfModelCapabilities;
147
+ /** See createReadFileTool's seat of the same name. */
148
+ bgOutputReadExemption?: (canonicalKey: string, ctx: {
149
+ taskId?: string;
150
+ principal?: string;
151
+ toolCallId?: string;
152
+ }) => boolean;
153
+ readDeny?: import("./read-deny.js").ReadDenyMatcher;
154
+ readFace?: import("./containment-face.js").ContainmentFace;
155
+ reminderMark?: string;
156
+ reminderDisclosureCounts?: ReminderDisclosureCounts;
157
+ /** The bare-form disclosure throttle windows, keyed by canonical file key. Owned by whoever builds the
158
+ * reader; every carrier that reads through the same reader shares them. Absent ⇒ a fresh map. */
159
+ disclosureWindows?: Map<string, number>;
160
+ }
161
+ export interface ModelFileReader {
162
+ readForModel(args: unknown, ctx: ReadForModelContext): Promise<ReadForModelResult>;
163
+ /** Apply the result's effects: the read-state entry (stamped `lastReadAt` and the transmission that
164
+ * carried it) and the disclosure windows / counters. Once per result: a second commit throws. */
165
+ commit(result: ReadForModelResult, at?: {
166
+ transmittedByToolCallId?: string;
167
+ now?: number;
168
+ }): void;
169
+ }
170
+ export declare function createModelFileReader(opts: ModelFileReaderOptions): ModelFileReader;
171
+ /** The Read carrier's sentence for one note — the bytes the Read tool has always emitted for it. The
172
+ * data-adjacent notes (range header, partial marker, fallback prefix, 0-cell placeholder, resize line,
173
+ * disclosure trailer) render without their joining separators; {@link renderReadToolResult} places them. */
174
+ export declare function renderReadNoteForTool(note: ReadNote, mark: string | undefined): string;
175
+ /** The Read tool's result for one read — the composition each lane has always returned. */
176
+ export declare function renderReadToolResult(r: ReadForModelResult, mark: string | undefined): ToolReturn;
6
177
  export declare function createReadFileTool(env: ExecutionEnv, state: ReadFileState, rootCanonical: string, cwdRef?: CwdRef, additionalRoots?: RootsSeat, imageDownsampler?: ReadImageDownsamplerOption, pdfCapabilities?: PdfModelCapabilities, bgOutputReadExemption?: (canonicalKey: string, ctx: {
7
178
  taskId?: string;
8
179
  principal?: string;