@sema-agent/core 7.5.0 → 7.5.2

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 (106) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/core/auto-mode.d.ts +9 -0
  3. package/dist/core/auto-mode.js +11 -0
  4. package/dist/core/checkpoint-store.js +5 -1
  5. package/dist/core/protocol-table.d.ts +5 -0
  6. package/dist/core/protocol-table.js +1 -0
  7. package/dist/core/runner/abort-race.d.ts +41 -0
  8. package/dist/core/runner/abort-race.js +38 -0
  9. package/dist/core/runner/checkpoint-scope.d.ts +44 -0
  10. package/dist/core/runner/checkpoint-scope.js +7 -0
  11. package/dist/core/runner/compaction-call-options.d.ts +1 -1
  12. package/dist/core/runner/content-ask-bindings.d.ts +27 -0
  13. package/dist/core/runner/content-ask-bindings.js +1 -0
  14. package/dist/core/runner/contracts.d.ts +1990 -0
  15. package/dist/core/runner/contracts.js +1 -0
  16. package/dist/core/runner/denial-limit-arms.d.ts +24 -1
  17. package/dist/core/runner/denial-limit-arms.js +21 -0
  18. package/dist/core/runner/derived-route-fallback.d.ts +34 -0
  19. package/dist/core/runner/derived-route-fallback.js +16 -0
  20. package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
  21. package/dist/core/runner/inherited-ask-grants.js +29 -0
  22. package/dist/core/runner/park-commit.d.ts +108 -0
  23. package/dist/core/runner/park-commit.js +32 -0
  24. package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
  25. package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
  26. package/dist/core/runner/prepare-acquire-reconcile.d.ts +1 -1
  27. package/dist/core/runner/prepare-ask-lane.d.ts +110 -0
  28. package/dist/core/runner/prepare-ask-lane.js +133 -0
  29. package/dist/core/runner/prepare-boundary-parks.d.ts +105 -0
  30. package/dist/core/runner/prepare-boundary-parks.js +169 -0
  31. package/dist/core/runner/prepare-caps-and-workflow.d.ts +170 -0
  32. package/dist/core/runner/prepare-caps-and-workflow.js +255 -0
  33. package/dist/core/runner/prepare-config-doors.d.ts +2 -10
  34. package/dist/core/runner/prepare-context-lane.d.ts +119 -0
  35. package/dist/core/runner/prepare-context-lane.js +230 -0
  36. package/dist/core/runner/prepare-defer-classify.d.ts +86 -0
  37. package/dist/core/runner/prepare-defer-classify.js +107 -0
  38. package/dist/core/runner/prepare-delegation-surface.d.ts +104 -0
  39. package/dist/core/runner/prepare-delegation-surface.js +144 -0
  40. package/dist/core/runner/prepare-execution-env.d.ts +54 -0
  41. package/dist/core/runner/prepare-execution-env.js +86 -0
  42. package/dist/core/runner/prepare-file-history.d.ts +95 -0
  43. package/dist/core/runner/prepare-file-history.js +383 -0
  44. package/dist/core/runner/prepare-gate-stations.d.ts +177 -0
  45. package/dist/core/runner/prepare-gate-stations.js +290 -0
  46. package/dist/core/runner/prepare-hands-readface.d.ts +9 -11
  47. package/dist/core/runner/prepare-hands-readface.js +1 -1
  48. package/dist/core/runner/prepare-inherited-gate.d.ts +268 -0
  49. package/dist/core/runner/prepare-inherited-gate.js +266 -0
  50. package/dist/core/runner/prepare-listings.d.ts +77 -0
  51. package/dist/core/runner/prepare-listings.js +76 -0
  52. package/dist/core/runner/prepare-lsp.d.ts +55 -0
  53. package/dist/core/runner/prepare-lsp.js +27 -0
  54. package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
  55. package/dist/core/runner/prepare-memory-engine-session.js +233 -0
  56. package/dist/core/runner/prepare-memory.d.ts +1 -1
  57. package/dist/core/runner/prepare-offload-wrappers.d.ts +62 -0
  58. package/dist/core/runner/prepare-offload-wrappers.js +45 -0
  59. package/dist/core/runner/prepare-park-ask.d.ts +164 -0
  60. package/dist/core/runner/prepare-park-ask.js +377 -0
  61. package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
  62. package/dist/core/runner/prepare-policy-chain.js +584 -0
  63. package/dist/core/runner/prepare-project-context.d.ts +119 -0
  64. package/dist/core/runner/prepare-project-context.js +148 -0
  65. package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
  66. package/dist/core/runner/prepare-prompt-assembly.js +162 -0
  67. package/dist/core/runner/prepare-prompt-inputs.d.ts +119 -0
  68. package/dist/core/runner/prepare-prompt-inputs.js +141 -0
  69. package/dist/core/runner/prepare-protocol-tools.d.ts +91 -0
  70. package/dist/core/runner/prepare-protocol-tools.js +179 -0
  71. package/dist/core/runner/prepare-question-face.d.ts +101 -0
  72. package/dist/core/runner/prepare-question-face.js +84 -0
  73. package/dist/core/runner/prepare-run-refs.d.ts +89 -0
  74. package/dist/core/runner/prepare-run-refs.js +39 -0
  75. package/dist/core/runner/prepare-safety-scan.d.ts +3 -7
  76. package/dist/core/runner/prepare-safety-scan.js +1 -2
  77. package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
  78. package/dist/core/runner/prepare-suspend-saga.js +308 -0
  79. package/dist/core/runner/prepare-task.d.ts +14 -1976
  80. package/dist/core/runner/prepare-task.js +182 -5162
  81. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +111 -0
  82. package/dist/core/runner/prepare-tool-disclosure-mount.js +219 -0
  83. package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
  84. package/dist/core/runner/prepare-turn-wiring.js +201 -0
  85. package/dist/core/runner/prepare-wiring-manifest.d.ts +192 -0
  86. package/dist/core/runner/prepare-wiring-manifest.js +247 -0
  87. package/dist/core/runner/prepare-workspace-restore.d.ts +3 -56
  88. package/dist/core/runner/prepare-workspace-restore.js +4 -38
  89. package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
  90. package/dist/core/runner/prompt-hash-salt.js +2 -0
  91. package/dist/core/runner/remote-env-retry.d.ts +29 -0
  92. package/dist/core/runner/remote-env-retry.js +16 -0
  93. package/dist/core/runner/rollback-stack.d.ts +32 -0
  94. package/dist/core/runner/rollback-stack.js +30 -0
  95. package/dist/core/runner/runtask.js +1 -1
  96. package/dist/core/runner/workspace-path.d.ts +33 -0
  97. package/dist/core/runner/workspace-path.js +22 -0
  98. package/dist/core/session.d.ts +12 -0
  99. package/dist/core/session.js +3 -0
  100. package/dist/core/tool-policy.d.ts +16 -0
  101. package/dist/core/tool-policy.js +3 -0
  102. package/dist/core/types.d.ts +2 -2
  103. package/dist/core/write-protect.js +3 -2
  104. package/package.json +6 -2
  105. /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
  106. /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * design/390 M9 — prepareTask's PROJECT-CONTEXT phase: the deployment's project memory snapshot (the
3
+ * `loadProjectMemory` seam: phase derivation, instruction sources, the framed + fenced project layer
4
+ * appended to the memory block), the skills roster (declaration rank, normalization, the 1MB load gate,
5
+ * the manifest-scope door, the `skill` tool mount), the two built-in memory pairs' mount-or-yield
6
+ * (shared-memory pair, engine-memory pair), and the skills-listing projection the run loop announces.
7
+ * The body is the driver's stretch moved verbatim; the interface below is the dependency list it had
8
+ * implicitly (design/238 R-1: a new read is an Input field, i.e. a review event).
9
+ *
10
+ * ASYNC BY CONTENT: the stretch awaits the session branch read, the seam itself and the read-state
11
+ * seeding (all inside the `loadProjectMemory` arm). It already sits past prepareTask's first await, so
12
+ * the one extra microtask the driver's `await` adds on the seam-less path opens no window any caller
13
+ * could reach (design/390 §1.5 S1 synchronicity rule: only pre-first-await stretches must stay sync).
14
+ *
15
+ * Throws pass through unchanged: `config.reserved_tool_name` (a caller tool named like the skill tool
16
+ * while skills are present) is the only refusal here; the seam's own throw and the seeding's are
17
+ * caught and forwarded to `deps.onError` (best-effort, never block the task).
18
+ */
19
+ import type { AgentTool } from "../../internal/harness.js";
20
+ import { type StoredSession } from "../session.js";
21
+ import type { RunnerDeps, TaskSpec, ToolSpec } from "../types.js";
22
+ import type { ActiveSkillScope } from "./active-skill-scope.js";
23
+ import type { AnnounceOnceSink, Prepared, PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
24
+ import { normalizeSkills } from "./synthetic-tools.js";
25
+ export interface PrepareProjectContextInput {
26
+ /** borrowed-readonly — the deployment seams this phase reads: `loadProjectMemory` (the snapshot),
27
+ * `onError` (the operator lane for a throwing seam / seeding / oversize skill), `sharedMemoryStores`
28
+ * (the shared-memory pair's switch: wired ⇒ mount). Receiver preserved (`deps.onError?.(…)`). */
29
+ deps: Pick<RunnerDeps, "loadProjectMemory" | "onError" | "sharedMemoryStores">;
30
+ /** borrowed-readonly — the REBOUND spec (`spec′`). Read: `sessionId` (phase derivation + the
31
+ * continuation projections), `skills` (the roster), `principal` (the shared-memory tools' trusted
32
+ * identity). Never mutated. */
33
+ spec: Pick<TaskSpec, "sessionId" | "skills" | "principal">;
34
+ /** borrowed-readonly — trusted internals: `agentName` (seam ctx), `inheritedManifestScope` (the
35
+ * parent's active skill frames, seeded as the base of the LIFO). `undefined` on a standalone prepare. */
36
+ internals: Pick<RunInternals, "agentName" | "inheritedManifestScope"> | undefined;
37
+ /** borrowed-readonly — the acquired session; only `getBranch()` is read (phase derivation walk).
38
+ * The read is best-effort: a throw keeps the provisional phase. */
39
+ session: Pick<StoredSession, "getBranch">;
40
+ /** borrowed-readonly — the acquired session's id (seam ctx cache key; operator-line context). */
41
+ sessionId: string;
42
+ /** borrowed-readonly — `spec.taskId ?? sessionId`: the shared-memory tools' trusted task identity. */
43
+ hostTaskId: string;
44
+ /** borrowed-readonly — the FINAL task root (post-restore): the seam's `cwd`. */
45
+ taskRootFinal: string;
46
+ /** borrowed-readonly — whether a real execution env is mounted (seam ctx flag). */
47
+ handsEnabled: boolean;
48
+ /** borrowed-readonly — the leg's effective-delegation facts; only `isNonForkChild` is read (the
49
+ * seam's `isSubagent` — one derivation shared with the prompt fact, never re-derived here). */
50
+ delegation: {
51
+ readonly isNonForkChild: boolean;
52
+ };
53
+ /** borrowed-readonly — the hands phase's read-state seeding closure (CREATED there, CALLED here for
54
+ * the snapshot's declared `seededFiles`); `undefined` on a hands-less task = nothing to seed. */
55
+ seedContextFiles: ((files: ReadonlyArray<{
56
+ path: string;
57
+ content: string;
58
+ }>) => Promise<void>) | undefined;
59
+ /** borrowed-readonly — the memory phase's composed block, as returned (recall discipline + engine
60
+ * files). This phase APPENDS the project layer and hands the result back as
61
+ * {@link PrepareProjectContextResult.memoryBlock}; the input value itself is never mutated. */
62
+ memoryBlockFromEngine: string | undefined;
63
+ /** borrowed-mutable — the run's LIFO skill-scope stack (identity is the contract: the scope policy
64
+ * and the `skill` tool alias this exact object). Mutated here: `push` of every inherited parent
65
+ * frame; after this phase only the `skill` tool's execute pushes/pops. */
66
+ skillScope: ActiveSkillScope;
67
+ /** borrowed-mutable — the shared mount array (later refs alias it; in-place contract). Mutated
68
+ * here: `push` of the `skill` tool, the shared-memory pair and the engine-memory pair. Occupancy
69
+ * is judged on this ALREADY-MOUNTED roster (names + aliases). */
70
+ tools: AgentTool[];
71
+ /** borrowed-readonly — the frozen task-start tool face; only `exclude` is read (pair yield ①). */
72
+ toolFaceSnapshot: Pick<ToolFaceSnapshot, "exclude">;
73
+ /** borrowed-readonly — the once-per-session announcement ledger; only its deduplicated `onError`
74
+ * sink is invoked (a pair's mount shape is a session-level fact, announced once). */
75
+ onceLedger: AnnounceOnceSink;
76
+ /** borrowed-readonly — the memory phase's engine-pair ToolSpecs (present iff the pair may mount). */
77
+ memoryTools: ToolSpec[] | undefined;
78
+ /** borrowed-readonly — the memory phase's session seat; only `!== undefined` is read (the
79
+ * blocked-mount disclosure fires only when a session mounted). */
80
+ memoryEngineSession: Prepared["memoryEngineSession"];
81
+ /** borrowed-readonly — the engine pair's early conjuncts (computed before the memory phase). */
82
+ memorySearchToolsPlanned: boolean;
83
+ /** borrowed-readonly — the engine-pair name the exclusion valve removed, if any (disclosure text). */
84
+ memoryPairExcludedName: string | undefined;
85
+ /** borrowed-readonly — the engine-pair name a declared tool already occupies, if any (disclosure text). */
86
+ memoryPairOccupiedName: string | undefined;
87
+ /** borrowed-readonly — the durable-resume carrier; read for `!== undefined` (seedAnnounced) and
88
+ * `seed.announcedListings.skills` (the removal-to-zero projection's gate). */
89
+ resume: Pick<PrepareResume, "seed"> | undefined;
90
+ }
91
+ export interface PrepareProjectContextResult {
92
+ /** owned — the memory block with the project layer appended at the tail (unchanged when the seam is
93
+ * unwired or returns null/blank). The driver binds it under a NEW name; the defer-classification
94
+ * stretch may still strip the recall-discipline segment from it. */
95
+ memoryBlock: string | undefined;
96
+ /** owned — the snapshot's declared instruction sources (non-empty only), for the run loop's
97
+ * instructions_change lane (`Prepared.instructionSources`). */
98
+ instructionSources: ReadonlyArray<{
99
+ path: string;
100
+ contentHash: string | null;
101
+ }> | undefined;
102
+ /** owned — the RAW snapshot text (pre-compose) for the compaction lanes' instruction-file seat
103
+ * (`Prepared.projectInstructionContent`). */
104
+ projectInstructionContent: string | undefined;
105
+ /** owned — the normalized, load-gated skills (first-wins dedupe, name-sorted). Consumed inside this
106
+ * phase (tool + listing); exposed as the phase's own product. */
107
+ skillSpecs: ReturnType<typeof normalizeSkills>;
108
+ /** owned — whether THIS task carries a manifested skill or inherits a parent frame (the skill-scope
109
+ * policy's activation predicate). */
110
+ hasSkillManifest: boolean;
111
+ /** owned — the skills-listing projection the run loop's announce lane consumes (`Prepared.skillsListing`). */
112
+ skillsListing: Prepared["skillsListing"];
113
+ /** owned — whether the shared-memory pair mounted here (the support-name pre-check reads it). */
114
+ sharedMemoryPairMounted: boolean;
115
+ /** owned — whether the engine-memory pair mounted here (the support-name pre-check reads it). */
116
+ memoryEnginePairMounted: boolean;
117
+ }
118
+ /** The M9 phase body — prepareTask's project-context stretch, verbatim (see the module header). */
119
+ export declare function prepareProjectContext(input: PrepareProjectContextInput): Promise<PrepareProjectContextResult>;
@@ -0,0 +1,148 @@
1
+ import { PROJECT_CONTEXT_FRAMING } from "../../prompts/default.js";
2
+ import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
3
+ import { composeMemoryBlock } from "../memory.js";
4
+ import { hasConversationContent } from "../session.js";
5
+ import { createSharedMemoryTools } from "../shared-memory/tools.js";
6
+ import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
7
+ import { defineTool } from "../tools.js";
8
+ import { SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
9
+ export async function prepareProjectContext(input) {
10
+ const { deps, spec, internals, session, sessionId, hostTaskId, taskRootFinal, handsEnabled, delegation, seedContextFiles, memoryBlockFromEngine, skillScope, tools, toolFaceSnapshot, onceLedger, memoryTools, memoryEngineSession, memorySearchToolsPlanned, memoryPairExcludedName, memoryPairOccupiedName, resume } = input;
11
+ let memoryBlock = memoryBlockFromEngine;
12
+ let instructionSources;
13
+ let projectInstructionContent;
14
+ if (deps.loadProjectMemory) {
15
+ let projectMemoryPhase = spec.sessionId ? "resume" : "fresh";
16
+ if (spec.sessionId) {
17
+ try {
18
+ const branch = await session.getBranch();
19
+ const hasConversation = hasConversationContent(branch);
20
+ if (!hasConversation)
21
+ projectMemoryPhase = "fresh";
22
+ else {
23
+ for (let i = branch.length - 1; i >= 0; i--) {
24
+ const e = branch[i];
25
+ if (e.type === "compaction") {
26
+ projectMemoryPhase = "post-compact";
27
+ break;
28
+ }
29
+ if (e.type === "message" && e.message.role === "user")
30
+ break;
31
+ }
32
+ }
33
+ }
34
+ catch {
35
+ }
36
+ }
37
+ let loaded = null;
38
+ try {
39
+ loaded = await Promise.resolve(deps.loadProjectMemory({
40
+ cwd: taskRootFinal,
41
+ handsEnabled,
42
+ isSubagent: delegation.isNonForkChild,
43
+ ...(internals?.agentName ? { agentName: internals.agentName } : {}),
44
+ sessionId,
45
+ phase: projectMemoryPhase,
46
+ }));
47
+ }
48
+ catch (err) {
49
+ deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
50
+ }
51
+ const projectMem = loaded !== null && typeof loaded === "object" ? loaded.content : loaded;
52
+ const seededFiles = loaded !== null && typeof loaded === "object" ? loaded.seededFiles : undefined;
53
+ const declaredSources = loaded !== null && typeof loaded === "object" ? loaded.instructionSources : undefined;
54
+ if (declaredSources !== undefined && declaredSources.length > 0)
55
+ instructionSources = declaredSources;
56
+ if (seededFiles?.length && seedContextFiles) {
57
+ try {
58
+ await seedContextFiles(seededFiles);
59
+ }
60
+ catch (err) {
61
+ deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
62
+ }
63
+ }
64
+ if (projectMem != null && projectMem.trim()) {
65
+ projectInstructionContent = projectMem;
66
+ const projectBlock = `${PROJECT_CONTEXT_FRAMING}\n\n${composeMemoryBlock(projectMem, "project")}`;
67
+ memoryBlock = memoryBlock ? `${memoryBlock}\n\n${projectBlock}` : projectBlock;
68
+ }
69
+ }
70
+ const declaredSkillRank = new Map();
71
+ for (const s of spec.skills ?? [])
72
+ if (!declaredSkillRank.has(s.name))
73
+ declaredSkillRank.set(s.name, declaredSkillRank.size);
74
+ const skillSpecs = normalizeSkills(spec.skills ?? []).filter((s) => {
75
+ if (s.content.length <= SKILL_CONTENT_MAX_CHARS)
76
+ return true;
77
+ deps.onError?.(new Error(`Skill "${s.name}" content is ${s.content.length} chars, over the ${SKILL_CONTENT_MAX_CHARS}-char load limit — skill not loaded (skills are never truncated; shrink the body or move material to attachments/files).`), { phase: "config", sessionId });
78
+ return false;
79
+ });
80
+ const inheritedFrames = internals?.inheritedManifestScope ?? [];
81
+ for (const frame of inheritedFrames) {
82
+ skillScope.push(frame);
83
+ }
84
+ const hasSkillManifest = skillSpecs.some((s) => s.manifest !== undefined) || inheritedFrames.length > 0;
85
+ if (skillSpecs.length > 0) {
86
+ if (tools.some((t) => t.name === SKILL_TOOL_NAME)) {
87
+ const e = new Error(`Tool name "${SKILL_TOOL_NAME}" is reserved when spec.skills is present.`);
88
+ e.code = "config.reserved_tool_name";
89
+ throw e;
90
+ }
91
+ tools.push(createSkillTool(skillSpecs, skillScope));
92
+ }
93
+ const sharedMemoryProvider = deps.sharedMemoryStores;
94
+ let sharedMemoryPairMounted = false;
95
+ if (sharedMemoryProvider !== undefined) {
96
+ const excluded = new Set(toolFaceSnapshot.exclude ?? []);
97
+ const mountedNameDomain = new Set();
98
+ for (const t of tools) {
99
+ mountedNameDomain.add(t.name);
100
+ for (const alias of t.aliases ?? [])
101
+ mountedNameDomain.add(alias);
102
+ }
103
+ const excludedName = SHARED_MEMORY_TOOL_NAMES.find((n) => excluded.has(n));
104
+ const occupiedName = SHARED_MEMORY_TOOL_NAMES.find((n) => mountedNameDomain.has(n));
105
+ if (excludedName !== undefined || occupiedName !== undefined) {
106
+ onceLedger.onError(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: ` +
107
+ (excludedName !== undefined
108
+ ? `excludeTools removes "${excludedName}"`
109
+ : `this task already declares a tool named "${occupiedName}" (the built-in yields to it)`) +
110
+ " — the pair mounts together or not at all."), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
111
+ }
112
+ else {
113
+ tools.push(...createSharedMemoryTools({
114
+ provider: sharedMemoryProvider,
115
+ context: { sessionId, taskId: hostTaskId, ...(spec.principal !== undefined ? { principal: spec.principal } : {}) },
116
+ }).map((s) => defineTool(s)));
117
+ sharedMemoryPairMounted = true;
118
+ }
119
+ }
120
+ let memoryEnginePairMounted = false;
121
+ if (memoryTools !== undefined && memoryTools.length > 0) {
122
+ tools.push(...memoryTools.map((s) => defineTool(s)));
123
+ memoryEnginePairMounted = true;
124
+ }
125
+ else if (memoryEngineSession !== undefined && !memorySearchToolsPlanned) {
126
+ onceLedger.onError(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: ` +
127
+ (memoryPairExcludedName !== undefined
128
+ ? `excludeTools removes "${memoryPairExcludedName}"`
129
+ : `this task already declares a tool named "${memoryPairOccupiedName}" (the built-in yields to it)`) +
130
+ " — these tools mount together or not at all."), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
131
+ }
132
+ const skillsListing = skillSpecs.length > 0
133
+ ? {
134
+ entries: skillSpecs.map((s) => ({
135
+ name: s.name,
136
+ description: s.description,
137
+ ...(s.files !== undefined ? { files: s.files.map((f) => ({ path: f.path })) } : {}),
138
+ ...(declaredSkillRank.get(s.name) !== undefined ? { declaredRank: declaredSkillRank.get(s.name) } : {}),
139
+ })),
140
+ seedAnnounced: resume !== undefined || spec.sessionId !== undefined,
141
+ }
142
+ :
143
+ (resume?.seed.announcedListings?.skills?.length ?? 0) > 0 || spec.sessionId !== undefined
144
+ ?
145
+ { entries: [], seedAnnounced: true }
146
+ : undefined;
147
+ return { memoryBlock, instructionSources, projectInstructionContent, skillSpecs, hasSkillManifest, skillsListing, sharedMemoryPairMounted, memoryEnginePairMounted };
148
+ }
@@ -0,0 +1,95 @@
1
+ import type { AgentTool } from "../../internal/harness.js";
2
+ import type { Model } from "../../internal/llm.js";
3
+ import { assemblePrompt, type AssembledPrompt } from "../../prompt-assembly/assemble.js";
4
+ import { type PromptEpochArtifact } from "../../prompt-assembly/artifact.js";
5
+ import { type EpochDeclaredSections } from "../../prompt-assembly/epoch.js";
6
+ import { type EnvironmentFacts, type PromptProvider, type PromptTextDeclaration } from "../../prompts/default.js";
7
+ import { type StoredSession } from "../session.js";
8
+ import type { RunnerDeps } from "../types.js";
9
+ import type { AnnounceOnceSink, Prepared, PrepareResume, PromptFeatureFlags, RunInternals } from "./contracts.js";
10
+ /** The center adoption a run composes under: the artifact and, when the source stamped one, its revision. */
11
+ export type CenterAdoption = {
12
+ artifact: PromptEpochArtifact;
13
+ sourceRevision?: string;
14
+ };
15
+ export interface PreparePromptAssemblyInput {
16
+ /** borrowed-mutable — the acquired session. Read: `getPromptEpoch` (the pin, twice — the center arm and the epoch
17
+ * resolution), `getBranch` (the freshness discriminator on the pinless arms). Writer here: `appendPromptEpoch`, the
18
+ * ONE pin write of this leg (only when the resolution migrated). The acquire phase and the run loop write it elsewhere. */
19
+ session: Pick<StoredSession, "getPromptEpoch" | "getBranch" | "appendPromptEpoch">;
20
+ /** borrowed-readonly — the deployment seats: `promptSource` (the center artifact store and its live candidate), `onError`
21
+ * (the assembly's warn channel, the prompt-constitution line, the missing-declaration disclosure). Receiver preserved. */
22
+ deps: Pick<RunnerDeps, "promptSource" | "onError">;
23
+ /** borrowed-readonly — the trusted spawn-side channel: the parent's center closure (`parentCenterArtifactDigest`,
24
+ * `parentCenterSourceRevision`), the top-level freshness facts (`parentTaskId`, `parentSessionId`) and the named-child
25
+ * fact the teammate flag reads (`explicitAgentName`). */
26
+ internals: Pick<RunInternals, "parentCenterArtifactDigest" | "parentCenterSourceRevision" | "parentTaskId" | "parentSessionId" | "explicitAgentName"> | undefined;
27
+ /** borrowed-readonly — the resume leg, or undefined; PRESENCE only (a resumed leg is never truly fresh). */
28
+ resume: Pick<PrepareResume, "seed"> | undefined;
29
+ /** borrowed-readonly — the resolved prompt provider (compared against the default for `isDefaultProvider`). */
30
+ provider: PromptProvider;
31
+ /** borrowed-readonly — the caller's role-layer prompt, or undefined. */
32
+ userSystemPrompt: string | undefined;
33
+ /** borrowed-readonly — the caller's appended prompt, or undefined. */
34
+ userAppendSystemPrompt: string | undefined;
35
+ /** borrowed-readonly — the harness-context flags; the two orchestration flags are re-derived against the post-exclusion
36
+ * roster at assembly. */
37
+ featureFlags: PromptFeatureFlags;
38
+ /** borrowed-readonly — the MCP instructions block, or undefined. */
39
+ mcpInstructionsBlock: string | undefined;
40
+ /** borrowed-readonly — the memory block as the assembly reads it (the recall paragraph already stripped when a
41
+ * retraction ran), or undefined. */
42
+ memoryBlock: string | undefined;
43
+ /** borrowed-readonly — the environment facts; `date` is the assembly's date, the block builder re-renders on a date change. */
44
+ envFacts: EnvironmentFacts;
45
+ /** borrowed-readonly — the resolved prompt profile (a runtime fact of the assembly). */
46
+ promptProfile: "simple" | "classic";
47
+ /** borrowed-readonly — the fable mitigations switch (a runtime fact of the assembly). */
48
+ fableMitigations: boolean;
49
+ /** borrowed-readonly — the run's ONE tool roster, read only: the assembly receives DETACHED copies, the audit and the
50
+ * manifest read it in place. Never mutated here. */
51
+ tools: AgentTool[];
52
+ /** borrowed-readonly — the task model; only `promptGuidance` (the model-guidance section). */
53
+ model: Pick<Model, "promptGuidance">;
54
+ /** borrowed-readonly — the acquired session id (every operator line's attribution). */
55
+ sessionId: string;
56
+ /** borrowed-readonly — the session's reminder provenance mark (the declaration the assembled prompt must render). */
57
+ reminderMark: string;
58
+ /** borrowed-readonly — the once-per-session ledger's operator sink (the collision audit's two lines). */
59
+ onceLedger: Pick<AnnounceOnceSink, "onError">;
60
+ }
61
+ export interface PreparePromptAssemblyResult {
62
+ /** borrowed-mutable — the LIVE system prompt (an accessor cell over this phase's closure variable). Writers: this
63
+ * phase's `renderWithDate` (the date re-render, called by the listings phase's date_change seam); the turn-wiring
64
+ * phase's center candidate `apply` (the compaction-boundary swap). Readers: the harness construction, the cache
65
+ * fingerprint, the turn snapshot's stable text, the prompt-overhead term. */
66
+ systemPromptSeat: {
67
+ current: string;
68
+ };
69
+ /** owned — the date re-render, or undefined for a provider whose assembly cannot re-render. Returns the new prompt and
70
+ * writes it into `systemPromptSeat`. */
71
+ renderWithDate: ((date: string) => string) | undefined;
72
+ /** owned — the physical block face of the assembled prompt, or undefined (the harness's systemBlocks and the turn
73
+ * snapshot's layout groups). */
74
+ systemBlocks: AssembledPrompt["systemBlocks"];
75
+ /** borrowed-mutable — the composition manifest (`Prepared.promptManifest`). Writers after this phase: the tool-disclosure
76
+ * phase writes `toolDisclosure` (when deferred tools exist); the turn-wiring phase backfills `snapshot` and `lowering`
77
+ * once the final wire tool list exists. */
78
+ promptManifest: Prepared["promptManifest"];
79
+ /** owned — the provider-declared sections in the epoch shape (id, slot, chars, opt-in contentHash; never text). */
80
+ epochDeclaredSections: EpochDeclaredSections;
81
+ /** owned — the resolved epoch's artifact digest, the turn snapshot's identity element. */
82
+ epochArtifactDigestForSnapshot: string;
83
+ /** owned — the re-invokable assemble-inputs BUILDER: the compaction-boundary candidate re-assembles with a new
84
+ * artifact's declarations through it (facts and tails identical; only the center closure differs). */
85
+ buildAssembleInputs: (centerDecls?: PromptTextDeclaration[]) => Parameters<typeof assemblePrompt>[0];
86
+ /** borrowed-mutable — the LIVE center adoption (an accessor cell over this phase's closure variable), or undefined.
87
+ * Writers: this phase (the three arms; the all-shadowed drop on the fresh arm); the turn-wiring phase's center
88
+ * candidate `apply` (the boundary swap / the explicit-disable rollback). Readers: the two late-bound ctx getters
89
+ * (inherited-gate, caps-and-workflow), the pin write, the candidate's same-artifact test. */
90
+ centerAdoptionRef: {
91
+ current: CenterAdoption | undefined;
92
+ };
93
+ }
94
+ /** The M13 phase body — prepareTask's prompt-assembly stretch, verbatim (see the module header). */
95
+ export declare function preparePromptAssembly(input: PreparePromptAssemblyInput): Promise<PreparePromptAssemblyResult>;
@@ -0,0 +1,162 @@
1
+ import { createHash } from "node:crypto";
2
+ import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
3
+ import { assemblePrompt } from "../../prompt-assembly/assemble.js";
4
+ import { artifactDeclarations } from "../../prompt-assembly/artifact.js";
5
+ import { resolveEpochAgainstBundled } from "../../prompt-assembly/epoch.js";
6
+ import { SEMA_DEFAULT_PACK } from "../../prompt-assembly/packs/sema-default.js";
7
+ import { auditToolCollisions, projectToolManifest } from "../../prompt-assembly/tool-catalog.js";
8
+ import { buildEnvironmentContext, defaultPromptProvider } from "../../prompts/default.js";
9
+ import { protocolOf } from "../protocol-table.js";
10
+ import { reminderMarkDeclaration } from "../reminder-mint.js";
11
+ import { hasConversationContent } from "../session.js";
12
+ import { PROMPT_HASH_SALT } from "./prompt-hash-salt.js";
13
+ export async function preparePromptAssembly(input) {
14
+ const { session, deps, internals, resume, provider, userSystemPrompt, userAppendSystemPrompt, featureFlags, mcpInstructionsBlock, memoryBlock, envFacts, promptProfile, fableMitigations, tools, model, sessionId, reminderMark, onceLedger } = input;
15
+ let centerAdoption;
16
+ let centerAdoptionFresh = false;
17
+ {
18
+ const failUnavailable = (digest, via) => {
19
+ const e = new Error(`prompt snapshot unavailable: ${via} center prompt artifact ${digest.slice(0, 23)}… has no verified resolvable copy (artifact store miss/corrupt${deps.promptSource === undefined ? "; RunnerDeps.promptSource is not configured" : ""}) — refusing to recompose a hybrid prompt (protocol §9.2)`);
20
+ e.code = "prompt.snapshot_unavailable";
21
+ throw e;
22
+ };
23
+ const pinnedEarly = await session.getPromptEpoch();
24
+ if (pinnedEarly?.centerArtifactDigest !== undefined) {
25
+ const art = await deps.promptSource?.get(pinnedEarly.centerArtifactDigest);
26
+ if (art === undefined)
27
+ failUnavailable(pinnedEarly.centerArtifactDigest, "this session is pinned to");
28
+ centerAdoption = { artifact: art, ...(pinnedEarly.sourceRevision !== undefined ? { sourceRevision: pinnedEarly.sourceRevision } : {}) };
29
+ }
30
+ else if (pinnedEarly === undefined) {
31
+ if (internals?.parentCenterArtifactDigest !== undefined) {
32
+ const art = await deps.promptSource?.get(internals.parentCenterArtifactDigest);
33
+ if (art === undefined)
34
+ failUnavailable(internals.parentCenterArtifactDigest, "the spawning parent adopted");
35
+ centerAdoption = {
36
+ artifact: art,
37
+ ...(internals.parentCenterSourceRevision !== undefined ? { sourceRevision: internals.parentCenterSourceRevision } : {}),
38
+ };
39
+ }
40
+ else if (resume === undefined && internals?.parentTaskId === undefined && internals?.parentSessionId === undefined) {
41
+ const candidate = deps.promptSource?.current();
42
+ if (candidate !== undefined && !hasConversationContent(await session.getBranch())) {
43
+ centerAdoption = { artifact: candidate.artifact, sourceRevision: candidate.sourceRevision };
44
+ centerAdoptionFresh = true;
45
+ }
46
+ }
47
+ }
48
+ }
49
+ const buildAssembleInputs = (centerDecls) => ({
50
+ provider,
51
+ isDefaultProvider: provider === defaultPromptProvider,
52
+ userSystemPrompt,
53
+ userAppendSystemPrompt,
54
+ tools: tools.map((t) => (t.aliases === undefined ? { ...t } : { ...t, aliases: [...t.aliases] })),
55
+ facts: {
56
+ supervisorEnabled: false,
57
+ teammateEnabled: internals?.explicitAgentName !== undefined && tools.some((t) => t.name === SEND_MESSAGE_TOOL_NAME),
58
+ ...featureFlags,
59
+ orchestrationEnabled: featureFlags.orchestrationEnabled && tools.some((t) => t.name === "Workflow"),
60
+ orchestrationDeferred: featureFlags.orchestrationDeferred && tools.some((t) => t.name === "Workflow"),
61
+ promptProfile,
62
+ fableMitigations,
63
+ },
64
+ mcpInstructionsBlock,
65
+ memoryBlock,
66
+ buildEnvBlock: (date) => buildEnvironmentContext({ ...envFacts, date }) || undefined,
67
+ date: envFacts.date,
68
+ ...(model.promptGuidance && model.promptGuidance.length > 0 ? { modelGuidance: model.promptGuidance.join("\n\n") } : {}),
69
+ onWarn: (message, phase) => deps.onError?.(new Error(message), { phase, sessionId }),
70
+ ...(centerDecls !== undefined ? { centerDeclarations: centerDecls } : {}),
71
+ });
72
+ const assembled = assemblePrompt(buildAssembleInputs(centerAdoption !== undefined ? artifactDeclarations(centerAdoption.artifact) : undefined));
73
+ if (centerAdoption !== undefined && assembled.centerMounted !== true) {
74
+ const ownsWholePrompt = assembled.constitution === "provider-assembled";
75
+ const digest = centerAdoption.artifact.artifactDigest.slice(0, 23);
76
+ if (ownsWholePrompt) {
77
+ const e = new Error(`center prompt artifact ${digest}… cannot mount: this PromptProvider owns the whole prompt (assembled-identity pass-through) — upgrade the provider to stableBlocks/stableSystem or remove RunnerDeps.promptSource for this deployment`);
78
+ e.code = "prompt.center_unmountable";
79
+ throw e;
80
+ }
81
+ deps.onError?.(new Error(centerAdoptionFresh
82
+ ? `prompt-constitution: center prompt artifact ${digest}… contributed NO section to this prompt — every section it declares collides with a PromptProvider stableBlocks declaration of the same id, and the deployment-local declaration wins. This session runs on the bundled prompt and is deliberately NOT pinned to the artifact (a pin claiming content the model never received would be durable and wrong). Rename the colliding section ids on either side to adopt it; the per-section collisions are reported individually on this channel.`
83
+ : `prompt-constitution: this session is pinned to center prompt artifact ${digest}…, but under the CURRENT PromptProvider every section that artifact declares collides with a stableBlocks declaration of the same id, so none of them compiled into this prompt. The session keeps its pin and continues on the bundled prompt — its epoch's declaredSections record what actually compiled. Rename the colliding section ids on either side to restore the artifact's content.`), { phase: "prompt-constitution", sessionId });
84
+ if (centerAdoptionFresh)
85
+ centerAdoption = undefined;
86
+ }
87
+ let systemPrompt = assembled.systemPrompt;
88
+ if (!systemPrompt.includes(reminderMarkDeclaration(reminderMark))) {
89
+ deps.onError?.(new Error("reminder-mark declaration missing from the assembled system prompt: this run's PromptProvider owns the prompt and did not render the mark declaration, so engine <system-reminder> tags carry a mark the model is never told about (the impersonation defense is inactive for this deployment). Render `reminderMarkDeclaration(ctx.reminderMark)` (exported; the value rides StablePromptContext.reminderMark) in the provider, or accept the disclosure."), { phase: "config", sessionId });
90
+ }
91
+ const renderWithDate = assembled.renderWithDate
92
+ ? (date) => {
93
+ systemPrompt = assembled.renderWithDate(date);
94
+ return systemPrompt;
95
+ }
96
+ : undefined;
97
+ const promptConstitution = assembled.constitution;
98
+ const promptBlocks = assembled.legacyBlocks;
99
+ const collisionAudit = auditToolCollisions(tools);
100
+ for (const y of collisionAudit.yields) {
101
+ onceLedger.onError(new Error(`tool mount: caller tool "${y.name}" shadows another tool with the same canonical name (deliberate last-write-wins yield — the later mount serves)`), { phase: "config", sessionId });
102
+ }
103
+ for (const c of collisionAudit.aliasCollisions) {
104
+ onceLedger.onError(new Error(`tool mount: alias "${c.alias}" is claimed by both "${c.owners[0]}" and "${c.owners[1]}" (design/141 warn face — name resolution serves the later mount)`), { phase: "config", sessionId });
105
+ }
106
+ const saltedHash = (text) => createHash("sha256").update(PROMPT_HASH_SALT).update(text).digest("hex").slice(0, 12);
107
+ const promptManifest = {
108
+ constitution: promptConstitution,
109
+ blocks: promptBlocks.map((b) => ({ id: b.id, chars: b.text.length, hash: saltedHash(b.text) })),
110
+ ...(assembled.sections
111
+ ? { sections: assembled.sections.map((s) => ({ id: s.id, slot: s.slot, carrier: s.carrier, cadence: s.cadence, cacheClass: s.cacheClass, chars: s.text.length, hash: saltedHash(s.text), ...(s.declaredContentHash !== undefined ? { contentHash: s.declaredContentHash } : {}) })) }
112
+ : {}),
113
+ tools: projectToolManifest(tools, (t) => protocolOf(t.name)?.id ?? "caller"),
114
+ };
115
+ const epochDeclaredSections = (assembled.sections ?? [])
116
+ .filter((s) => s.origin === "provider")
117
+ .map((s) => ({ id: s.id, slot: s.slot, chars: s.text.length, ...(s.declaredContentHash !== undefined ? { contentHash: s.declaredContentHash } : {}) }));
118
+ let epochArtifactDigestForSnapshot = "";
119
+ {
120
+ const pinned = await session.getPromptEpoch();
121
+ const resolved = resolveEpochAgainstBundled(pinned, SEMA_DEFAULT_PACK, epochDeclaredSections);
122
+ epochArtifactDigestForSnapshot = resolved.descriptor.artifactDigest;
123
+ if (resolved.migrated) {
124
+ const priorConversation = pinned === undefined ? hasConversationContent(await session.getBranch()) : true;
125
+ const activatedBy = pinned === undefined && !priorConversation ? "session_start" : "legacy_migration";
126
+ await session.appendPromptEpoch({
127
+ ...resolved.descriptor,
128
+ activatedBy,
129
+ ...(centerAdoption !== undefined
130
+ ? {
131
+ centerArtifactDigest: centerAdoption.artifact.artifactDigest,
132
+ ...(centerAdoption.sourceRevision !== undefined ? { sourceRevision: centerAdoption.sourceRevision } : {}),
133
+ }
134
+ : {}),
135
+ });
136
+ }
137
+ }
138
+ return {
139
+ systemPromptSeat: {
140
+ get current() {
141
+ return systemPrompt;
142
+ },
143
+ set current(v) {
144
+ systemPrompt = v;
145
+ },
146
+ },
147
+ renderWithDate,
148
+ systemBlocks: assembled.systemBlocks,
149
+ promptManifest,
150
+ epochDeclaredSections,
151
+ epochArtifactDigestForSnapshot,
152
+ buildAssembleInputs,
153
+ centerAdoptionRef: {
154
+ get current() {
155
+ return centerAdoption;
156
+ },
157
+ set current(v) {
158
+ centerAdoption = v;
159
+ },
160
+ },
161
+ };
162
+ }