@sema-agent/core 5.16.0 → 5.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +224 -0
  2. package/dist/agents/peer-admission.d.ts +58 -0
  3. package/dist/agents/peer-admission.js +175 -0
  4. package/dist/agents/retain-ledger.d.ts +1 -1
  5. package/dist/agents/retain-ledger.js +9 -1
  6. package/dist/agents/send-message-tool.d.ts +8 -0
  7. package/dist/agents/send-message-tool.js +171 -21
  8. package/dist/agents/subagent.d.ts +13 -0
  9. package/dist/agents/subagent.js +90 -5
  10. package/dist/core/ask-question.js +16 -1
  11. package/dist/core/canonical-json.js +176 -14
  12. package/dist/core/checkpoint-store.d.ts +14 -0
  13. package/dist/core/checkpoint-store.js +73 -0
  14. package/dist/core/hooks.d.ts +4 -1
  15. package/dist/core/hooks.js +24 -6
  16. package/dist/core/mailbox-store.d.ts +2 -0
  17. package/dist/core/mailbox-store.js +2 -2
  18. package/dist/core/mcp.d.ts +1 -0
  19. package/dist/core/mcp.js +15 -3
  20. package/dist/core/runner/prepare-task.d.ts +4 -0
  21. package/dist/core/runner/prepare-task.js +169 -46
  22. package/dist/core/runner/runtask.js +13 -1
  23. package/dist/core/runner/turn-attachments.d.ts +2 -1
  24. package/dist/core/runner/turn-attachments.js +9 -6
  25. package/dist/core/session-reconcile.js +19 -1
  26. package/dist/core/shared-memory/contract.d.ts +17 -0
  27. package/dist/core/shared-memory/contract.js +138 -0
  28. package/dist/core/shared-memory/normalize.d.ts +73 -0
  29. package/dist/core/shared-memory/normalize.js +259 -0
  30. package/dist/core/shared-memory/tools.d.ts +7 -0
  31. package/dist/core/shared-memory/tools.js +289 -0
  32. package/dist/core/shared-memory/types.d.ts +95 -0
  33. package/dist/core/shared-memory/types.js +18 -0
  34. package/dist/core/task-notification.d.ts +3 -0
  35. package/dist/core/task-registry-agent.d.ts +1 -1
  36. package/dist/core/task-registry-agent.js +2 -1
  37. package/dist/core/task-registry.d.ts +1 -1
  38. package/dist/core/task-registry.js +2 -0
  39. package/dist/core/tool-policy.d.ts +9 -0
  40. package/dist/core/tool-policy.js +28 -8
  41. package/dist/core/types.d.ts +8 -0
  42. package/dist/core/untrusted-text.d.ts +1 -0
  43. package/dist/core/untrusted-text.js +10 -0
  44. package/dist/core/wiring-manifest.js +2 -2
  45. package/dist/engine/harness/agent-harness.d.ts +1 -0
  46. package/dist/engine/harness/agent-harness.js +21 -2
  47. package/dist/engine/llm/validation.js +121 -5
  48. package/dist/engine/loop/agent-loop.d.ts +2 -0
  49. package/dist/engine/loop/agent-loop.js +17 -4
  50. package/dist/index.d.ts +4 -0
  51. package/dist/index.js +4 -0
  52. package/dist/prompts/supervisor.d.ts +1 -1
  53. package/dist/prompts/supervisor.js +1 -1
  54. package/dist/stores/cc/mailbox-store.js +4 -0
  55. package/dist/stores/file/checkpoint-store.d.ts +1 -0
  56. package/dist/stores/file/checkpoint-store.js +1 -0
  57. package/dist/stores/file/mailbox-store.js +2 -2
  58. package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -0
  59. package/dist/tools/fs/bash-readonly-classifier.js +11 -10
  60. package/dist/tools/fs/fs-bash.js +6 -6
  61. package/dist/tools/fs/fs-read.d.ts +1 -1
  62. package/dist/tools/fs/fs-read.js +4 -3
  63. package/dist/tools/fs/fs-shared.d.ts +3 -0
  64. package/dist/tools/fs/fs-shared.js +8 -1
  65. package/dist/tools/fs/fs-write.js +8 -8
  66. package/dist/tools/fs/index.d.ts +1 -0
  67. package/dist/tools/fs/index.js +1 -1
  68. package/dist/tools/fs/safety.d.ts +1 -0
  69. package/dist/tools/fs/safety.js +9 -2
  70. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { Type } from "typebox";
3
3
  import { defineTool, errorResult } from "../../core/tools.js";
4
4
  import { sha256, resolveKey, violationText, violationDetails, requireRead, checkStale, checkEditMatch, checkNoChange, fileArgPath, resolveQuoteMatch, adaptNewStringQuotes, resolveEscapeMatch, adaptNewStringEscapes, escapeMatchWasAttempted, ESCAPE_MATCH_MISS_NOTE, deletionOldString, countOccurrences, READ_REFUSED_ESCAPE_HINT, } from "./safety.js";
5
5
  import { decodeTextBytes, encodeTextForFile, normalizeEditText, normalizeFileText } from "./encoding.js";
6
- import { MAX_EDIT_BYTES, formatByteSize, decodeEditBytes, persistedTextOf, notReadRefusalText, enoentMessage, FILE_STATE_TRAILER, FILE_PATH_PARAMS, ipynbRedirect, countLines, } from "./fs-shared.js";
6
+ import { MAX_EDIT_BYTES, decodeEditBytes, tooLargeToEditMessage, truncatedUtf16BodyMessage, persistedTextOf, notReadRefusalText, enoentMessage, FILE_STATE_TRAILER, FILE_PATH_PARAMS, ipynbRedirect, countLines, } from "./fs-shared.js";
7
7
  async function gateToolWrite(hook, tool, path, key, content) {
8
8
  if (hook === undefined)
9
9
  return undefined;
@@ -91,21 +91,21 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
91
91
  }
92
92
  const editInfo = await env.fileInfo(r.key, ctx.signal);
93
93
  if (editInfo.ok && editInfo.value.size > MAX_EDIT_BYTES) {
94
- return errorResult(`Error (Edit): "${path}" is too large to edit (${formatByteSize(editInfo.value.size)}). Maximum editable file size is ${formatByteSize(MAX_EDIT_BYTES)}.`);
94
+ return errorResult(tooLargeToEditMessage(path, editInfo.value.size));
95
95
  }
96
96
  if (singleOld === "") {
97
97
  const preBin = await env.readBinaryFile(r.key, ctx.signal);
98
98
  if (!preBin.ok)
99
99
  return errorResult(`Error (Edit): cannot read "${path}": ${preBin.error.message}`);
100
100
  if (preBin.value.byteLength > MAX_EDIT_BYTES) {
101
- return errorResult(`Error (Edit): "${path}" is too large to edit (${formatByteSize(preBin.value.byteLength)}). Maximum editable file size is ${formatByteSize(MAX_EDIT_BYTES)}.`);
101
+ return errorResult(tooLargeToEditMessage(path, preBin.value.byteLength));
102
102
  }
103
103
  const preDecResult = decodeEditBytes(preBin.value, path);
104
104
  if (!preDecResult.ok)
105
105
  return errorResult(preDecResult.message);
106
106
  const preDec = preDecResult.value;
107
107
  if (preDec.malformed)
108
- return errorResult(`Error (Edit): "${path}" has a truncated UTF-16 body; repair/convert it with bash first.`);
108
+ return errorResult(truncatedUtf16BodyMessage("Edit", path));
109
109
  if (preDec.text.trim() !== "")
110
110
  return errorResult(`Error (Edit): Cannot create new file - file already exists.`);
111
111
  const priorRead = state.get(r.key);
@@ -141,14 +141,14 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
141
141
  if (!readBin.ok)
142
142
  return errorResult(`Error (Edit): cannot read "${path}": ${readBin.error.message}`);
143
143
  if (readBin.value.byteLength > MAX_EDIT_BYTES) {
144
- return errorResult(`Error (Edit): "${path}" is too large to edit (${formatByteSize(readBin.value.byteLength)}). Maximum editable file size is ${formatByteSize(MAX_EDIT_BYTES)}.`);
144
+ return errorResult(tooLargeToEditMessage(path, readBin.value.byteLength));
145
145
  }
146
146
  const decodedResult = decodeEditBytes(readBin.value, path);
147
147
  if (!decodedResult.ok)
148
148
  return errorResult(decodedResult.message);
149
149
  const decoded = decodedResult.value;
150
150
  if (decoded.malformed)
151
- return errorResult(`Error (Edit): "${path}" has a truncated UTF-16 body; repair/convert it with bash first.`);
151
+ return errorResult(truncatedUtf16BodyMessage("Edit", path));
152
152
  const original = decoded.text;
153
153
  const entry = state.get(r.key);
154
154
  const readWasTruncated = entry?.truncated === true;
@@ -256,7 +256,7 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
256
256
  return errorResult(`Error (Write): cannot re-read "${path}" to verify it is unchanged: ${readBin.error.message}`);
257
257
  const decodedPrev = decodeTextBytes(readBin.value);
258
258
  if (decodedPrev.malformed)
259
- return errorResult(`Error (Write): "${path}" has a truncated UTF-16 body; repair/convert it with bash first.`);
259
+ return errorResult(truncatedUtf16BodyMessage("Write", path));
260
260
  const stale = checkStale(state.get(r.key), sha256(decodedPrev.text));
261
261
  if (stale)
262
262
  return errorResult(violationText("Write", stale));
@@ -340,7 +340,7 @@ export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additi
340
340
  return errorResult(`Error (NotebookEdit): cannot read "${notebook_path}": ${readBin.error.message}`);
341
341
  const decodedNb = decodeTextBytes(readBin.value);
342
342
  if (decodedNb.malformed)
343
- return errorResult(`Error (NotebookEdit): "${notebook_path}" has a truncated UTF-16 body; repair/convert it with bash first.`);
343
+ return errorResult(truncatedUtf16BodyMessage("NotebookEdit", notebook_path));
344
344
  const nbText = decodedNb.text;
345
345
  const stale = checkStale(state.get(r.key), sha256(nbText));
346
346
  if (stale)
@@ -34,6 +34,7 @@ export interface HandsToolkitOptions {
34
34
  autoBackgroundOnTimeout?: boolean;
35
35
  readImageDownsampler?: ReadImageDownsamplerOption;
36
36
  pdfModelCapabilities?: PdfModelCapabilities;
37
+ readCyberReminder?: boolean;
37
38
  beforeWrite?: BeforeWriteHook;
38
39
  monitorToolActive?: boolean;
39
40
  }
@@ -30,7 +30,7 @@ export function createHandsToolkit(env, readFileState, rootCanonical, opts = {})
30
30
  ...(opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {}),
31
31
  }, env);
32
32
  const tools = [
33
- createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption),
33
+ createReadFileTool(env, readFileState, rootCanonical, readOnly ? undefined : cwdRef, readFaceRoots, opts.readImageDownsampler, opts.pdfModelCapabilities, bgOutputReadExemption, opts.readCyberReminder),
34
34
  ];
35
35
  if (!readOnly) {
36
36
  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));
@@ -82,4 +82,5 @@ export declare function escapeMatchWasAttempted(oldString: string): boolean;
82
82
  export declare const ESCAPE_MATCH_MISS_NOTE = "\n(note: Edit also tried swapping \\uXXXX escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. Re-read the file and copy the exact surrounding text.)";
83
83
  export declare function adaptNewStringQuotes(matchedOld: string, newString: string): string;
84
84
  export declare function deletionOldString(content: string, oldString: string, newString: string): string;
85
+ export declare const EDIT_ECHO_MAX_CHARS = 200;
85
86
  export declare function checkEditMatch(content: string, oldString: string, replaceAll: boolean, truncated: boolean): FsViolation | undefined;
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
2
3
  export function fileArgPath(args) {
3
4
  const a = args;
4
5
  const fp = a?.file_path;
@@ -577,6 +578,12 @@ export function deletionOldString(content, oldString, newString) {
577
578
  return oldString + "\n";
578
579
  return oldString;
579
580
  }
581
+ export const EDIT_ECHO_MAX_CHARS = 200;
582
+ function boundEditEcho(oldString) {
583
+ if (oldString.length <= EDIT_ECHO_MAX_CHARS)
584
+ return oldString;
585
+ return `${sliceHeadSafe(oldString, EDIT_ECHO_MAX_CHARS)}… (truncated)`;
586
+ }
580
587
  export function checkEditMatch(content, oldString, replaceAll, truncated) {
581
588
  if (oldString === "") {
582
589
  return { code: "invalid", message: "old_string must not be empty." };
@@ -585,13 +592,13 @@ export function checkEditMatch(content, oldString, replaceAll, truncated) {
585
592
  if (n === 0) {
586
593
  return {
587
594
  code: "ambiguous_edit",
588
- message: `String to replace not found in file.${truncated ? " (note: the read was truncated — it may be in the unshown portion)" : ""}\nString: ${oldString}`,
595
+ message: `String to replace not found in file.${truncated ? " (note: the read was truncated — it may be in the unshown portion)" : ""}\nString: ${boundEditEcho(oldString)}`,
589
596
  };
590
597
  }
591
598
  if (n > 1 && !replaceAll) {
592
599
  return {
593
600
  code: "ambiguous_edit",
594
- message: `Found ${n} matches of the string to replace, but replace_all is false. To replace all occurrences, set replace_all to true. To replace only one occurrence, please provide more context to uniquely identify the instance.${truncated ? " (the read was truncated; some matches may be in the unshown portion)" : ""}\nString: ${oldString}`,
601
+ message: `Found ${n} matches of the string to replace, but replace_all is false. To replace all occurrences, set replace_all to true. To replace only one occurrence, please provide more context to uniquely identify the instance.${truncated ? " (the read was truncated; some matches may be in the unshown portion)" : ""}\nString: ${boundEditEcho(oldString)}`,
595
602
  };
596
603
  }
597
604
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.16.0",
3
+ "version": "5.17.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",