@sema-agent/core 5.35.0 → 5.36.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 (34) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/agents/subagent.js +29 -2
  3. package/dist/core/auto-compaction.d.ts +23 -0
  4. package/dist/core/auto-compaction.js +8 -0
  5. package/dist/core/checkpoint-store.d.ts +16 -0
  6. package/dist/core/context-guard.d.ts +41 -0
  7. package/dist/core/context-guard.js +76 -0
  8. package/dist/core/memory-engine/engine.js +1 -1
  9. package/dist/core/park-selfcheck.d.ts +5 -0
  10. package/dist/core/runner/assemble-result.d.ts +3 -0
  11. package/dist/core/runner/assemble-result.js +3 -0
  12. package/dist/core/runner/git-status-frame.d.ts +219 -0
  13. package/dist/core/runner/git-status-frame.js +212 -0
  14. package/dist/core/runner/prepare-task.d.ts +16 -0
  15. package/dist/core/runner/prepare-task.js +27 -34
  16. package/dist/core/runner/runtask.js +266 -5
  17. package/dist/core/task-registry-agent.d.ts +15 -0
  18. package/dist/core/task-registry-agent.js +9 -0
  19. package/dist/core/task-registry.d.ts +3 -0
  20. package/dist/core/task-registry.js +4 -1
  21. package/dist/core/types.d.ts +27 -7
  22. package/dist/engine/harness/types.d.ts +65 -1
  23. package/dist/engine/harness/types.js +20 -0
  24. package/dist/engine/session/import-validate.js +10 -1
  25. package/dist/engine/session/session.d.ts +37 -1
  26. package/dist/engine/session/session.js +56 -1
  27. package/dist/internal/harness-types.d.ts +1 -0
  28. package/dist/internal/harness.d.ts +2 -0
  29. package/dist/internal/harness.js +2 -0
  30. package/dist/prompt-assembly/epoch.js +1 -1
  31. package/dist/prompt-assembly/event-registry.js +1 -0
  32. package/dist/prompts/default.d.ts +20 -7
  33. package/dist/prompts/default.js +2 -7
  34. package/package.json +1 -1
@@ -66,6 +66,26 @@ export class AgentHarnessError extends Error {
66
66
  this.code = code;
67
67
  }
68
68
  }
69
+ export const GIT_ANNOUNCEMENT_MAX_ENTRY_ID_CHARS = 256;
70
+ export function normalizeGitAnnouncement(v) {
71
+ if (typeof v !== "object" || v === null)
72
+ return undefined;
73
+ const src = v;
74
+ if (src.kind !== "full" && src.kind !== "degraded" && src.kind !== "unavailable" && src.kind !== "non-repo")
75
+ return undefined;
76
+ if (typeof src.hash !== "string" || !/^sha256:[0-9a-f]{64}$/.test(src.hash))
77
+ return undefined;
78
+ if (src.entryId !== undefined && (typeof src.entryId !== "string" || src.entryId.length === 0 || src.entryId.length > GIT_ANNOUNCEMENT_MAX_ENTRY_ID_CHARS))
79
+ return undefined;
80
+ if (src.pending !== undefined && src.pending !== true)
81
+ return undefined;
82
+ return {
83
+ kind: src.kind,
84
+ hash: src.hash,
85
+ ...(src.entryId !== undefined ? { entryId: src.entryId } : {}),
86
+ ...(src.pending === true ? { pending: true } : {}),
87
+ };
88
+ }
69
89
  export const WORKSPACE_STATE_MAX_PATH_CHARS = 4096;
70
90
  export function normalizeWorkspaceState(value) {
71
91
  if (value === null || typeof value !== "object")
@@ -1,4 +1,4 @@
1
- import { SessionError, normalizeAnnouncedListing, normalizeWorkspaceState, normalizeCompactionStateCarrier, isValidThinkingLevelChange, isValidModelChange, isOptionalDisplayString, } from "../harness/types.js";
1
+ import { SessionError, normalizeAnnouncedListing, normalizeGitAnnouncement, normalizeWorkspaceState, normalizeCompactionStateCarrier, isValidThinkingLevelChange, isValidModelChange, isOptionalDisplayString, } from "../harness/types.js";
2
2
  import { leafIdAfterEntry } from "./storage-base.js";
3
3
  import { parseSessionTimestampMs } from "./timestamps.js";
4
4
  import { flattenableUserText, normalizeEngineSegments } from "../../core/untrusted-text.js";
@@ -87,6 +87,11 @@ export class StreamingImportValidator {
87
87
  throw new SessionError("invalid_session", `workspace_state entry "${e.id}" is structurally invalid`);
88
88
  }
89
89
  }
90
+ else if (e.type === "git_announcement") {
91
+ if (!normalizeGitAnnouncement(e)) {
92
+ throw new SessionError("invalid_session", `git_announcement entry "${e.id}" is structurally invalid`);
93
+ }
94
+ }
90
95
  if (e.type === "thinking_level_change" && !isValidThinkingLevelChange(e)) {
91
96
  throw new SessionError("invalid_session", `thinking_level_change entry "${e.id}" carries an invalid thinkingLevel`);
92
97
  }
@@ -119,6 +124,10 @@ export class StreamingImportValidator {
119
124
  if (restatedListings !== undefined && !normalizeAnnouncedListing(restatedListings)) {
120
125
  throw new SessionError("invalid_session", `compaction "${e.id}" carries a structurally invalid announcedListings restatement`);
121
126
  }
127
+ const restatedGit = e.details?.gitAnnouncement;
128
+ if (restatedGit !== undefined && !normalizeGitAnnouncement(restatedGit)) {
129
+ throw new SessionError("invalid_session", `compaction "${e.id}" carries a structurally invalid gitAnnouncement restatement`);
130
+ }
122
131
  const carrier = e.details;
123
132
  const shaped = normalizeCompactionStateCarrier(e.details);
124
133
  if (carrier?.thinkingLevel !== undefined && shaped.thinkingLevel === undefined) {
@@ -1,6 +1,6 @@
1
1
  import type { ImageContent, TextContent } from "../llm/index.js";
2
2
  import type { AgentMessage } from "../loop/types.js";
3
- import type { Session, SessionContext, SessionMetadata, SessionStorage, SessionTreeEntry, WorkspaceState } from "../harness/types.js";
3
+ import type { GitAnnouncementState, Session, SessionContext, SessionMetadata, SessionStorage, SessionTreeEntry, WorkspaceState } from "../harness/types.js";
4
4
  import type { BuildContextOptions } from "../harness/types.js";
5
5
  import type { PromptEpochDescriptor } from "../../prompt-assembly/epoch.js";
6
6
  /** Build model context from the active session branch and its latest state markers.
@@ -45,6 +45,31 @@ export declare class StoredSession<TMetadata extends SessionMetadata = SessionMe
45
45
  skills?: readonly string[];
46
46
  models?: readonly string[];
47
47
  }): Promise<string>;
48
+ /**
49
+ * Git-status frame announced state (branch-authority mirror): persist the `(kind, hash)` tuple —
50
+ * announced form carries the frame's own entry id; a compaction restates the tuple as `pending`
51
+ * inside its `details.gitAnnouncement` instead (same-CAS with the new baseline). Called
52
+ * best-effort at every frame COMMIT point (append receipt in hand) — an append failure degrades
53
+ * to a conservative re-announcement on the next leg (duplicate-tolerant), never fails the run.
54
+ */
55
+ appendGitAnnouncement(state: GitAnnouncementState): Promise<string>;
56
+ /**
57
+ * Branch-authority read of the git announced state: the nearest carrier walking back from the
58
+ * leaf (a `git_announcement` entry or a compaction's `details.gitAnnouncement` restatement;
59
+ * SNAPSHOT semantics — first hit wins). The pending gate is a MANDATORY one: a nearest carrier
60
+ * that is pending, or an announced carrier whose frame entryId is NOT on the active branch (a
61
+ * rewind cut the frame while a descendant mirror survived), reads as `status:"pending"` — the
62
+ * caller must conservatively re-announce rather than trust a mirror whose frame the model cannot
63
+ * see. Malformed carriers are skipped (defense in depth behind the import door). Undefined ⇒ no
64
+ * carrier visible on this branch (pre-migration session / bounded-tail floor cut every carrier) —
65
+ * callers fall back to the checkpoint mirror rung, then to conservative re-announcement.
66
+ */
67
+ getGitAnnouncement(): Promise<{
68
+ kind: GitAnnouncementState["kind"];
69
+ hash: string;
70
+ status: "announced" | "pending";
71
+ entryId?: string;
72
+ } | undefined>;
48
73
  /**
49
74
  * design/155 (cli [1580]): persist the settle-time workspace state (tracked cwd + active
50
75
  * EnterWorktree session) as a first-class typed entry. Called best-effort at task settle when the
@@ -115,3 +140,14 @@ export declare class StoredSession<TMetadata extends SessionMetadata = SessionMe
115
140
  appendLabel(targetId: string, label: string | undefined): Promise<string>;
116
141
  appendSessionName(name: string): Promise<string>;
117
142
  }
143
+ /**
144
+ * Falsification round 1 (F3) — is `entryId` not merely ON the active branch but inside the region
145
+ * a context rebuild can actually contain? A branch walk sees the whole tree; `buildSessionContext`
146
+ * replays only [latest compaction's firstKeptEntryId .. leaf] (plus the summary). A git frame that
147
+ * sits BEFORE the latest compaction's kept tail is therefore gone from every future request even
148
+ * though its id is a legitimate branch member — an announced mirror pointing at it must read as
149
+ * PENDING (re-announce), or the announced hash would suppress re-sends of bytes the model can no
150
+ * longer see. Shared by the session mirror walk and the run loop's checkpoint seeding rung (one
151
+ * rule, both readers). Conservative on damage: an unresolvable firstKeptEntryId reads as invisible.
152
+ */
153
+ export declare function gitFrameContextVisible(branch: SessionTreeEntry[], entryId: string): boolean;
@@ -1,5 +1,5 @@
1
1
  import { asAgentMessage, createCompactionSummaryMessage, createCustomMessage, } from "../harness/messages.js";
2
- import { SessionError, isValidModelChange, normalizeAnnouncedListing, normalizeCompactionStateCarrier, normalizeWorkspaceState } from "../harness/types.js";
2
+ import { SessionError, isValidModelChange, normalizeAnnouncedListing, normalizeCompactionStateCarrier, normalizeGitAnnouncement, normalizeWorkspaceState } from "../harness/types.js";
3
3
  import { normalizePromptEpoch } from "../../prompt-assembly/epoch.js";
4
4
  import { budgetInvokedSkillsRetention, readElidedMessages, readRetainedInvokedSkills, renderInvokedSkillsRetention, } from "../compaction/utils.js";
5
5
  const RETENTION_CLAMP_DEFAULT_CHARS_PER_TOKEN = 4;
@@ -181,6 +181,39 @@ export class StoredSession {
181
181
  ...shaped,
182
182
  });
183
183
  }
184
+ async appendGitAnnouncement(state) {
185
+ const shaped = normalizeGitAnnouncement(state);
186
+ if (!shaped) {
187
+ throw new SessionError("invalid_entry", "appendGitAnnouncement: state is not a structurally valid git announcement (closed kind, sha256 hash, bounded entryId)");
188
+ }
189
+ return this.appendTypedEntry({
190
+ type: "git_announcement",
191
+ id: await this.storage.createEntryId(),
192
+ parentId: await this.storage.getLeafId(),
193
+ timestamp: new Date().toISOString(),
194
+ ...shaped,
195
+ });
196
+ }
197
+ async getGitAnnouncement() {
198
+ const branch = await this.getBranch();
199
+ for (let i = branch.length - 1; i >= 0; i--) {
200
+ const entry = branch[i];
201
+ let carried;
202
+ if (entry.type === "git_announcement") {
203
+ carried = normalizeGitAnnouncement(entry);
204
+ }
205
+ else if (entry.type === "compaction") {
206
+ carried = normalizeGitAnnouncement(entry.details?.gitAnnouncement);
207
+ }
208
+ if (carried === undefined)
209
+ continue;
210
+ const announced = carried.pending !== true && carried.entryId !== undefined && gitFrameContextVisible(branch, carried.entryId);
211
+ return announced
212
+ ? { kind: carried.kind, hash: carried.hash, status: "announced", entryId: carried.entryId }
213
+ : { kind: carried.kind, hash: carried.hash, status: "pending" };
214
+ }
215
+ return undefined;
216
+ }
184
217
  async appendWorkspaceState(state) {
185
218
  const shaped = normalizeWorkspaceState(state);
186
219
  if (!shaped) {
@@ -314,3 +347,25 @@ export class StoredSession {
314
347
  });
315
348
  }
316
349
  }
350
+ export function gitFrameContextVisible(branch, entryId) {
351
+ let frameIdx = -1;
352
+ let compactionIdx = -1;
353
+ let firstKeptId;
354
+ for (let i = 0; i < branch.length; i++) {
355
+ const e = branch[i];
356
+ if (e.id === entryId)
357
+ frameIdx = i;
358
+ if (e.type === "compaction") {
359
+ compactionIdx = i;
360
+ firstKeptId = e.firstKeptEntryId;
361
+ }
362
+ }
363
+ if (frameIdx === -1)
364
+ return false;
365
+ if (compactionIdx === -1 || frameIdx > compactionIdx)
366
+ return true;
367
+ if (firstKeptId === undefined)
368
+ return false;
369
+ const keptIdx = branch.findIndex((e) => e.id === firstKeptId);
370
+ return keptIdx !== -1 && frameIdx >= keptIdx;
371
+ }
@@ -12,4 +12,5 @@ export type { AgentHarnessEvent, CompactionSettings, ExecutionEnv, ExecutionErro
12
12
  export type { ExecutionEnvExecOptions, ExecResult } from "../engine/harness/types.js";
13
13
  export type { SessionWriteOptions, CompactionEntry } from "../engine/harness/types.js";
14
14
  export type { ActiveWorktreeSession, WorkspaceState } from "../engine/harness/types.js";
15
+ export type { GitAnnouncementKind, GitAnnouncementState } from "../engine/harness/types.js";
15
16
  export type { SessionForkOptions } from "../engine/harness/types.js";
@@ -23,3 +23,5 @@ export { InMemorySessionStorage } from "../engine/session/memory-storage.js";
23
23
  export { InMemorySessionRepo } from "../engine/session/memory-repo.js";
24
24
  export { uuidv7 } from "../engine/session/uuid.js";
25
25
  export { SessionError } from "../engine/harness/types.js";
26
+ export { normalizeGitAnnouncement } from "../engine/harness/types.js";
27
+ export { gitFrameContextVisible } from "../engine/session/session.js";
@@ -12,3 +12,5 @@ export { InMemorySessionStorage } from "../engine/session/memory-storage.js";
12
12
  export { InMemorySessionRepo } from "../engine/session/memory-repo.js";
13
13
  export { uuidv7 } from "../engine/session/uuid.js";
14
14
  export { SessionError } from "../engine/harness/types.js";
15
+ export { normalizeGitAnnouncement } from "../engine/harness/types.js";
16
+ export { gitFrameContextVisible } from "../engine/session/session.js";
@@ -50,7 +50,7 @@ const PROBE_INPUTS_BASE = {
50
50
  roleBase: "\u0000probe-role\u0000",
51
51
  roleAppend: "\u0000probe-append\u0000",
52
52
  mcpInstructionsBlock: "\u0000probe-mcp\u0000",
53
- environmentBlock: "\u0000probe-env\u0000",
53
+ environmentBlock: "\u0000probe-env@2\u0000",
54
54
  memoryBlock: "\u0000probe-memory\u0000",
55
55
  modelGuidance: "\u0000probe-guidance\u0000",
56
56
  };
@@ -13,6 +13,7 @@ export const EVENT_PROMPT_REGISTRY = new Map([
13
13
  { kind: "skills_listing", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "on", rendererRef: "turn-attachments.ts#renderSkillsListingDelta" },
14
14
  { kind: "mcp_instructions", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpInstructionsDelta" },
15
15
  { kind: "mcp_dropped_tools", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpDroppedTools" },
16
+ { kind: "git_status", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "git-status-frame.ts#renderGitStatusFrameBody" },
16
17
  { kind: "limit_approach_converge", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#limitApproachFrames" },
17
18
  { kind: "limit_approach_deliver", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#limitApproachFrames" },
18
19
  { kind: "final_verification", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#final-verification" },
@@ -271,9 +271,13 @@ export interface EnvironmentFacts {
271
271
  cwd?: string;
272
272
  isGitRepo?: boolean;
273
273
  /** design/99 §E14 — the current git branch (remote = the container's repo). `"HEAD (detached)"` when on a
274
- * detached HEAD. Absent when not a git repo / no git. A prepare-time snapshot, like cwd (may go stale mid-run). */
274
+ * detached HEAD. Absent when not a git repo / no git. A prepare-time snapshot, like cwd (may go stale mid-run).
275
+ * env-tail migration (#254 shape): NO LONGER RENDERED by {@link buildEnvironmentContext} — the
276
+ * branch is a turn-dynamic fact and rides the `git_status` frame instead; the field remains as a
277
+ * structured data carrier for callers that assembled it. */
275
278
  gitBranch?: string;
276
- /** design/99 §E14 — whether the working tree has uncommitted changes (`git status --porcelain` non-empty). */
279
+ /** design/99 §E14 — whether the working tree has uncommitted changes (`git status --porcelain` non-empty).
280
+ * env-tail migration: NO LONGER RENDERED here (turn-dynamic; rides the `git_status` frame). */
277
281
  gitDirty?: boolean;
278
282
  /** design/99 §E14 — the working tree's toplevel (`git rev-parse --show-toplevel`). Lets the agent/shell know
279
283
  * it may be in a linked worktree rather than the main checkout. */
@@ -319,10 +323,10 @@ export interface EnvironmentFacts {
319
323
  /** A1 — outbound-network posture; `none` renders an explicit downloads-will-fail caveat (TB
320
324
  * autopsy 2026-07-05: agents burned budget retrying downloads in a no-egress sandbox). */
321
325
  sandboxEgress?: "none" | "allowlist" | "full";
322
- /** H4 (CC 2.1.198 git snapshot) — the pre-rendered {@link buildGitSnapshot} block, appended to the
323
- * `# Environment` block as its own paragraph. A PREPARE-TIME snapshot by contract (its first line
324
- * self-declares "will not update during the conversation"); the date_change re-render carries it
325
- * unchanged. Absent when not a git repo / the probe failed (whole section skipped, CC-same). */
326
+ /** H4 (CC 2.1.198 git snapshot) — the pre-rendered {@link buildGitSnapshot} block. env-tail
327
+ * migration (#254 shape): NO LONGER RENDERED by {@link buildEnvironmentContext} the snapshot is
328
+ * the turn-dynamic git view and rides the `git_status` frame (re-sent only when the rendered view
329
+ * changes); the field remains as a structured data carrier for callers that assembled it. */
326
330
  gitSnapshot?: string;
327
331
  /** [1451] B-half — per-lane resume-continuity facts (deployment-supplied via `TaskSpec.envFacts.resumeFacts`,
328
332
  * copied by prepare ONLY on a durable-resume leg). Each present field renders one honest sentence; absent
@@ -353,11 +357,20 @@ export interface EnvironmentFacts {
353
357
  export declare function buildScratchpadSection(scratchpadDir: string): string;
354
358
  /** CC 2.1.198 `Juo` — the git status truncation bound (chars) for {@link buildGitSnapshot}. */
355
359
  export declare const GIT_STATUS_MAX_CHARS = 2000;
360
+ /** The CC-verbatim first paragraph of the git snapshot (CC 2.1.198 `Quo` head sentence). Kept as a
361
+ * named constant so the git-status FRAME renderer (the snapshot's carrier since the env-tail
362
+ * migration) can swap exactly this paragraph for its own update-semantics preamble — under the
363
+ * frame protocol the snapshot IS re-sent when the visible view changes, so the "will not update"
364
+ * claim would be false there. {@link buildGitSnapshot} itself still renders it byte-exact. */
365
+ export declare const GIT_SNAPSHOT_CC_PREAMBLE = "This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.";
356
366
  /**
357
367
  * H4 — the CC 2.1.198 ENV git snapshot block (B1/B2; template + truncation copy verified against
358
368
  * observed Claude Code v2.1.198 behavior). Pure renderer — the Runner gathers the raw values through the
359
369
  * ExecutionEnv seam (remote = the container's repo) and calls this once per prepareTask (a durable
360
- * resume re-prepares → re-snapshots, CC new-session 同型).
370
+ * resume re-prepares → re-snapshots, CC new-session 同型). env-tail migration (#254 shape): the
371
+ * rendered block now feeds the `git_status` TURN FRAME (src/core/runner/git-status-frame.ts, which
372
+ * swaps the {@link GIT_SNAPSHOT_CC_PREAMBLE} for an update-semantics preamble) instead of the
373
+ * `# Environment` system-prompt section — the template body itself stays CC-verbatim.
361
374
  *
362
375
  * Sanitization (deliberate CC deviation — CC injects raw): status/log/branch/user are REPO-controlled
363
376
  * text (filenames, commit subjects, a hostile clone's config) landing in the TRUSTED prompt region →
@@ -196,6 +196,7 @@ Only use \`/tmp\` if the user explicitly requests it.
196
196
  The scratchpad directory is session-specific, isolated from the user's project, and can generally be used without permission prompts. Treat it as ephemeral — it may not survive a long suspension or a resume on a different worker; keep durable outputs in the working directory.`;
197
197
  }
198
198
  export const GIT_STATUS_MAX_CHARS = 2000;
199
+ export const GIT_SNAPSHOT_CC_PREAMBLE = "This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.";
199
200
  export function buildGitSnapshot(p) {
200
201
  const tool = p.statusToolName ?? "Bash";
201
202
  const status = sanitizeUntrustedText(p.status.trim());
@@ -205,7 +206,7 @@ export function buildGitSnapshot(p) {
205
206
  `\n... (truncated because it exceeds 2k characters. If you need more information, run "git status" using ${tool})`
206
207
  : status;
207
208
  return [
208
- "This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.",
209
+ GIT_SNAPSHOT_CC_PREAMBLE,
209
210
  `Current branch: ${inlineUntrusted(p.branch)}`,
210
211
  `Main branch (you will usually use this for PRs): ${inlineUntrusted(p.mainBranch)}`,
211
212
  ...(p.userName ? [`Git user: ${inlineUntrusted(p.userName)}`] : []),
@@ -255,10 +256,6 @@ export function buildEnvironmentContext(facts) {
255
256
  }
256
257
  if (facts.isGitRepo !== undefined)
257
258
  lines.push(`Is a git repository: ${facts.isGitRepo ? "yes" : "no"}`);
258
- if (facts.gitBranch)
259
- lines.push(`Git branch: ${inlineUntrusted(facts.gitBranch)}`);
260
- if (facts.gitDirty !== undefined)
261
- lines.push(`Git working tree: ${facts.gitDirty ? "has uncommitted changes" : "clean"}`);
262
259
  if (facts.gitWorktreeRoot)
263
260
  lines.push(`Git worktree root: ${inlineUntrusted(facts.gitWorktreeRoot)}`);
264
261
  if (facts.isLinkedWorktree) {
@@ -302,8 +299,6 @@ export function buildEnvironmentContext(facts) {
302
299
  }
303
300
  const base = lines.length > 0 ? `# Environment\n${lines.join("\n")}` : "";
304
301
  const sections = [base, ...(facts.scratchpadDir ? [buildScratchpadSection(facts.scratchpadDir)] : [])].filter((s) => s.length > 0);
305
- if (facts.gitSnapshot)
306
- sections.push(facts.gitSnapshot);
307
302
  return sections.join("\n\n");
308
303
  }
309
304
  export const CODE_AGENT_PROMPT = `You are a capable software-engineering agent that acts through tools.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.35.0",
3
+ "version": "5.36.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",