@agentex/agent 0.0.21 → 0.0.23

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 (77) hide show
  1. package/README.md +26 -1
  2. package/dist/goals/controller.d.ts +106 -0
  3. package/dist/goals/controller.d.ts.map +1 -0
  4. package/dist/goals/controller.js +375 -0
  5. package/dist/goals/controller.js.map +1 -0
  6. package/dist/goals/index.d.ts +6 -0
  7. package/dist/goals/index.d.ts.map +1 -0
  8. package/dist/goals/index.js +4 -0
  9. package/dist/goals/index.js.map +1 -0
  10. package/dist/goals/normalize.d.ts +72 -0
  11. package/dist/goals/normalize.d.ts.map +1 -0
  12. package/dist/goals/normalize.js +138 -0
  13. package/dist/goals/normalize.js.map +1 -0
  14. package/dist/goals/sentinel.d.ts +39 -0
  15. package/dist/goals/sentinel.d.ts.map +1 -0
  16. package/dist/goals/sentinel.js +77 -0
  17. package/dist/goals/sentinel.js.map +1 -0
  18. package/dist/index.d.ts +5 -2
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/providers/_shared/http-agent.d.ts.map +1 -1
  23. package/dist/providers/_shared/http-agent.js +1 -0
  24. package/dist/providers/_shared/http-agent.js.map +1 -1
  25. package/dist/providers/acp/index.d.ts.map +1 -1
  26. package/dist/providers/acp/index.js +1 -0
  27. package/dist/providers/acp/index.js.map +1 -1
  28. package/dist/providers/acp/session.d.ts.map +1 -1
  29. package/dist/providers/acp/session.js +29 -0
  30. package/dist/providers/acp/session.js.map +1 -1
  31. package/dist/providers/claude/index.d.ts.map +1 -1
  32. package/dist/providers/claude/index.js +3 -1
  33. package/dist/providers/claude/index.js.map +1 -1
  34. package/dist/providers/claude/parse.d.ts +61 -0
  35. package/dist/providers/claude/parse.d.ts.map +1 -1
  36. package/dist/providers/claude/parse.js +105 -0
  37. package/dist/providers/claude/parse.js.map +1 -1
  38. package/dist/providers/claude/session.d.ts +37 -1
  39. package/dist/providers/claude/session.d.ts.map +1 -1
  40. package/dist/providers/claude/session.js +219 -2
  41. package/dist/providers/claude/session.js.map +1 -1
  42. package/dist/providers/codex/index.d.ts.map +1 -1
  43. package/dist/providers/codex/index.js +3 -1
  44. package/dist/providers/codex/index.js.map +1 -1
  45. package/dist/providers/codex/parse.d.ts.map +1 -1
  46. package/dist/providers/codex/parse.js +74 -3
  47. package/dist/providers/codex/parse.js.map +1 -1
  48. package/dist/providers/codex/session.d.ts +31 -1
  49. package/dist/providers/codex/session.d.ts.map +1 -1
  50. package/dist/providers/codex/session.js +166 -6
  51. package/dist/providers/codex/session.js.map +1 -1
  52. package/dist/providers/cursor/index.d.ts.map +1 -1
  53. package/dist/providers/cursor/index.js +1 -0
  54. package/dist/providers/cursor/index.js.map +1 -1
  55. package/dist/providers/openclaw/index.d.ts.map +1 -1
  56. package/dist/providers/openclaw/index.js +1 -0
  57. package/dist/providers/openclaw/index.js.map +1 -1
  58. package/dist/providers/opencode/http-session.d.ts.map +1 -1
  59. package/dist/providers/opencode/http-session.js +29 -0
  60. package/dist/providers/opencode/http-session.js.map +1 -1
  61. package/dist/providers/opencode/index.d.ts.map +1 -1
  62. package/dist/providers/opencode/index.js +3 -0
  63. package/dist/providers/opencode/index.js.map +1 -1
  64. package/dist/providers/pi/index.d.ts.map +1 -1
  65. package/dist/providers/pi/index.js +3 -0
  66. package/dist/providers/pi/index.js.map +1 -1
  67. package/dist/providers/pi/session.d.ts +9 -1
  68. package/dist/providers/pi/session.d.ts.map +1 -1
  69. package/dist/providers/pi/session.js +32 -2
  70. package/dist/providers/pi/session.js.map +1 -1
  71. package/dist/providers/process/index.d.ts.map +1 -1
  72. package/dist/providers/process/index.js +1 -0
  73. package/dist/providers/process/index.js.map +1 -1
  74. package/dist/types.d.ts +230 -0
  75. package/dist/types.d.ts.map +1 -1
  76. package/dist/types.js.map +1 -1
  77. package/package.json +1 -1
package/README.md CHANGED
@@ -64,7 +64,7 @@ Providers fall into three tiers:
64
64
 
65
65
  > `cursor` stays one-shot until `cursor-agent` ships an ACP mode; it can then move to Tier 2 via `extends: "acp"`.
66
66
 
67
- Capabilities (sessions, modes, skills, workspaces, MCP, model discovery, quota probing, instructions, concurrent send, cancel queued messages) are declared on each module's `capabilities` field — check `provider.capabilities` to branch on what's supported. ACP providers set `dynamicCapabilities: true` (their real capability set is negotiated at the ACP `initialize` handshake). Skill-aware providers also report:
67
+ Capabilities (sessions, modes, skills, workspaces, MCP, model discovery, quota probing, instructions, concurrent send, cancel queued messages, stop task) are declared on each module's `capabilities` field — check `provider.capabilities` to branch on what's supported. ACP providers set `dynamicCapabilities: true` (their real capability set is negotiated at the ACP `initialize` handshake). Skill-aware providers also report:
68
68
 
69
69
  - `skillInventory` — `"provider-init"` for Claude's runtime inventory, `"local-discovery"` for Codex, or `"none"`.
70
70
  - `skillInvocation` — `"native-slash"` for Claude, `"expanded-prompt"` for Codex, `"configured-only"`, or `"unsupported"`.
@@ -416,6 +416,31 @@ else console.log("Too late — the agent already drained it.");
416
416
 
417
417
  > **Race note.** Cancellation is best-effort. If the CLI drained the message mid-turn (Claude's `query.ts` between-tool-batches drain) before your `cancel()` request landed, you get `{ cancelled: false }` — and the message will be visible to the model as a `<system-reminder>`. The library does not unmount what the model has already seen.
418
418
 
419
+ ### Stopping one background task
420
+
421
+ A turn can spawn work that outlives it — a backgrounded shell (`next dev`, a test watcher) or an async subagent. For providers with `capabilities.stopTask = true` (Claude only), `session.stopTask(taskId)` kills **one** such task without disturbing the session or its other tasks. This is distinct from `interrupt()` (ends the whole active turn, and can't reach a detached background process) and `close()` (kills the entire session).
422
+
423
+ ```typescript
424
+ // taskId comes from a background-task lifecycle event (see below).
425
+ const { stopped } = await session.stopTask(taskId);
426
+ if (stopped) console.log("Asked the CLI to kill that task.");
427
+ ```
428
+
429
+ `stopTask()` is always callable. For providers with `stopTask = false` it returns `{ stopped: false }` immediately. For Claude it sends a `stop_task` control_request; the **CLI/harness** owns the process and performs the kill (the model is never in the loop). The acknowledgement carries no payload, so `stopped: true` just means "accepted" — an unknown or already-ended `taskId` (or a closed session) yields `{ stopped: false }`. The task's terminal status arrives asynchronously on the event stream as its next `task_updated` / `task_notification`.
430
+
431
+ Background-task lifecycle events (`task_started`, `task_progress`, `task_updated`, `task_notification`) reach you as `type: "unknown"` StreamEvents (Claude emits them as `system` subtypes; only `system`+`init` gets its own typed variant, so the rest fall through the forward-compat escape hatch with the payload on `raw`). Use `getClaudeTaskDetails(event)` to decode one into typed fields — `{ phase, taskId, taskType, status, usage, … }` — or `null` if it isn't a task event. It's a stateless per-event decode, not a reducer: `status` reflects only the event in hand (and `task_updated` is a sparse `patch`), so collapsing a task's events into one current state over its lifetime is the consumer's job.
432
+
433
+ ```typescript
434
+ import { getClaudeTaskDetails } from "@agentex/agent";
435
+
436
+ createSession({
437
+ onEvent(event) {
438
+ const task = getClaudeTaskDetails(event);
439
+ if (task?.phase === "started") console.log("background task:", task.taskId, task.description);
440
+ },
441
+ });
442
+ ```
443
+
419
444
  ### Graceful shutdown with `drain()`
420
445
 
421
446
  `close()` kills the process (SIGTERM → SIGKILL after `graceSec`, default 5) — fine for "stop now," wrong when a tool is mid-flight. `drain()` is the graceful stop: it refuses new `send()` calls (they throw), waits for any in-flight turn's `result` to settle, then closes. Use it for budget gates, `SIGTERM` handlers, and schedule pauses where a running turn should finish rather than be cut off.
@@ -0,0 +1,106 @@
1
+ import type { ClearGoalResult, GoalCapability, GoalOptions, GoalState, SendHandle, SetGoalResult, StreamEvent, TurnResult } from "../types.js";
2
+ /**
3
+ * Everything a session must supply so the `GoalController` can run goals on its
4
+ * behalf. The two wiring seams every session already has — an event-dispatch
5
+ * point and a turn-settle point — are exposed via `dispatch` and the session
6
+ * calling `observe`/`onTurnSettled`.
7
+ */
8
+ export interface GoalControllerDeps {
9
+ /** Provider type, for `goal_status` event base fields + display. */
10
+ providerType: string;
11
+ /** Current session/thread id for event base fields. */
12
+ getSessionId: () => string | null;
13
+ /**
14
+ * Drive one turn — a continuation, the initial kickoff, the default
15
+ * sentinel's meta-turn, or (for native providers) the `/goal` command itself.
16
+ * Usually `session.send` bound to the session.
17
+ */
18
+ send: (message: string) => Promise<SendHandle>;
19
+ /**
20
+ * Push a synthetic `goal_status` event to the host through the SAME channel
21
+ * the session uses for parsed events. Only the controller calls this, and
22
+ * only for emulation transitions — native transitions come from the parser.
23
+ */
24
+ dispatch: (event: StreamEvent) => void;
25
+ /** Transcript path for sentinel context. */
26
+ getTranscriptPath?: () => string | null;
27
+ /** This provider's goal capability (undefined → treated as emulated). */
28
+ capability?: GoalCapability;
29
+ /**
30
+ * Native arm. Implement for providers with native goal support (Claude sends
31
+ * `/goal <objective>`; Codex seeds thread state). Return true if armed
32
+ * natively — the controller then relies on parser-emitted `goal_status`
33
+ * events and does NOT run the emulation loop. Return false to fall back to
34
+ * emulation. Only invoked for `enforce:"provider"` with no custom sentinel.
35
+ */
36
+ armNative?: (objective: string) => Promise<boolean>;
37
+ /** Native clear (Claude sends `/goal clear`; Codex clears thread state). */
38
+ clearNative?: (reason: "cleared" | "blocked") => Promise<void>;
39
+ }
40
+ /**
41
+ * Capability descriptor for providers with no native goal surface. The library
42
+ * emulation engine enforces the goal via a sentinel + continuation loop.
43
+ */
44
+ export declare const EMULATED_GOAL_CAPABILITY: GoalCapability;
45
+ /**
46
+ * Provider-agnostic goal engine. Each session constructs one and delegates
47
+ * `setGoal`/`clearGoal`/`getGoal` to it, calls `observe(event)` for every
48
+ * normalized event it dispatches, and `onTurnSettled(result)` whenever a turn
49
+ * resolves. See internal-docs/spec-goals.md §8.
50
+ */
51
+ export declare class GoalController {
52
+ private readonly deps;
53
+ private state;
54
+ private mode;
55
+ private sentinel;
56
+ private maxIterations;
57
+ private iterations;
58
+ /** Guards re-entrancy: meta/nested settles during evaluation are ignored. */
59
+ private evaluating;
60
+ /** One-shot: skip advancing the loop for the next settle (set on interrupt). */
61
+ private suspendNext;
62
+ /** Bumped on every set/clear so a stale in-flight loop cancels itself. */
63
+ private generation;
64
+ constructor(deps: GoalControllerDeps);
65
+ getGoal(): GoalState | null;
66
+ /**
67
+ * True when a non-terminal goal is active. Sessions use this to decide whether
68
+ * to parse + `observe()` native goal events even when the host attached no
69
+ * `onEvent` handler — so `getGoal()` stays accurate without a subscriber.
70
+ */
71
+ isTracking(): boolean;
72
+ /**
73
+ * Signal that the active turn was interrupted/aborted by the host. For an
74
+ * emulated goal this pauses the loop for one settle so the interrupt doesn't
75
+ * immediately trigger a fresh continuation turn. The goal stays active; the
76
+ * next normal turn (or a new setGoal) resumes it. clearGoal() abandons it.
77
+ */
78
+ notifyInterrupted(): void;
79
+ /**
80
+ * Restore goal state on resume — reporting only. Use with `goalStateFromEvent`
81
+ * / `latestGoalFromEvents` after reading a resumed session's transcript so
82
+ * `getGoal()` reflects the prior goal. To resume ENFORCEMENT (re-arm the
83
+ * sentinel/loop), call `setGoal` again; hydrate does not restart the loop.
84
+ */
85
+ hydrate(state: GoalState): void;
86
+ setGoal(objective: string, options?: GoalOptions): Promise<SetGoalResult>;
87
+ clearGoal(options?: {
88
+ reason?: "cleared" | "blocked";
89
+ }): Promise<ClearGoalResult>;
90
+ /**
91
+ * Called by the session for every normalized event it dispatches to the host.
92
+ * In native mode the parser is the source of truth for goal_status; adopt it.
93
+ */
94
+ observe(event: StreamEvent): void;
95
+ /**
96
+ * Called by the session whenever a turn settles. Only the emulation engine
97
+ * acts here; native/advisory goals are driven by provider events.
98
+ */
99
+ onTurnSettled(turn: TurnResult): Promise<void>;
100
+ private kickoff;
101
+ private makeDefaultSentinel;
102
+ /** Build, record, and dispatch a synthetic goal_status transition. */
103
+ private transition;
104
+ private buildEvent;
105
+ }
106
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/goals/controller.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,WAAW,EAEX,SAAS,EACT,UAAU,EACV,aAAa,EACb,WAAW,EACX,UAAU,EACX,MAAM,aAAa,CAAC;AAerB;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,YAAY,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,4CAA4C;IAC5C,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACxC,yEAAyE;IACzE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,4EAA4E;IAC5E,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChE;AAID;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,cAMtC,CAAC;AAcF;;;;;GAKG;AACH,qBAAa,cAAc;IAab,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZjC,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,aAAa,CAAM;IAC3B,OAAO,CAAC,UAAU,CAAK;IACvB,6EAA6E;IAC7E,OAAO,CAAC,UAAU,CAAS;IAC3B,gFAAgF;IAChF,OAAO,CAAC,WAAW,CAAS;IAC5B,0EAA0E;IAC1E,OAAO,CAAC,UAAU,CAAK;gBAEM,IAAI,EAAE,kBAAkB;IAErD,OAAO,IAAI,SAAS,GAAG,IAAI;IAI3B;;;;OAIG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;;OAKG;IACH,iBAAiB,IAAI,IAAI;IAIzB;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAKzB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;IA0G7E,SAAS,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;KAAO,GAAG,OAAO,CAAC,eAAe,CAAC;IAuD3F;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAuBjC;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA2DpD,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,mBAAmB;IAS3B,sEAAsE;IACtE,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,UAAU;CAwBnB"}
@@ -0,0 +1,375 @@
1
+ import { GOAL_OBJECTIVE_MAX, goalStateFromEvent, isTerminalGoalStatus, } from "./normalize.js";
2
+ import { buildKickoffMessage, createDefaultSentinel, defaultNudge, runSentinel, } from "./sentinel.js";
3
+ /**
4
+ * Capability descriptor for providers with no native goal surface. The library
5
+ * emulation engine enforces the goal via a sentinel + continuation loop.
6
+ */
7
+ export const EMULATED_GOAL_CAPABILITY = {
8
+ mechanism: "emulated",
9
+ enforced: true,
10
+ statuses: ["active", "paused", "met", "blocked", "cleared"],
11
+ clears: "manual",
12
+ telemetry: false,
13
+ };
14
+ function nowIso() {
15
+ return new Date().toISOString();
16
+ }
17
+ async function safeBool(p) {
18
+ try {
19
+ return await p;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ /**
26
+ * Provider-agnostic goal engine. Each session constructs one and delegates
27
+ * `setGoal`/`clearGoal`/`getGoal` to it, calls `observe(event)` for every
28
+ * normalized event it dispatches, and `onTurnSettled(result)` whenever a turn
29
+ * resolves. See internal-docs/spec-goals.md §8.
30
+ */
31
+ export class GoalController {
32
+ deps;
33
+ state = null;
34
+ mode = "idle";
35
+ sentinel = null;
36
+ maxIterations = 12;
37
+ iterations = 0;
38
+ /** Guards re-entrancy: meta/nested settles during evaluation are ignored. */
39
+ evaluating = false;
40
+ /** One-shot: skip advancing the loop for the next settle (set on interrupt). */
41
+ suspendNext = false;
42
+ /** Bumped on every set/clear so a stale in-flight loop cancels itself. */
43
+ generation = 0;
44
+ constructor(deps) {
45
+ this.deps = deps;
46
+ }
47
+ getGoal() {
48
+ return this.state;
49
+ }
50
+ /**
51
+ * True when a non-terminal goal is active. Sessions use this to decide whether
52
+ * to parse + `observe()` native goal events even when the host attached no
53
+ * `onEvent` handler — so `getGoal()` stays accurate without a subscriber.
54
+ */
55
+ isTracking() {
56
+ return this.state !== null && !isTerminalGoalStatus(this.state.status);
57
+ }
58
+ /**
59
+ * Signal that the active turn was interrupted/aborted by the host. For an
60
+ * emulated goal this pauses the loop for one settle so the interrupt doesn't
61
+ * immediately trigger a fresh continuation turn. The goal stays active; the
62
+ * next normal turn (or a new setGoal) resumes it. clearGoal() abandons it.
63
+ */
64
+ notifyInterrupted() {
65
+ if (this.mode === "emulate")
66
+ this.suspendNext = true;
67
+ }
68
+ /**
69
+ * Restore goal state on resume — reporting only. Use with `goalStateFromEvent`
70
+ * / `latestGoalFromEvents` after reading a resumed session's transcript so
71
+ * `getGoal()` reflects the prior goal. To resume ENFORCEMENT (re-arm the
72
+ * sentinel/loop), call `setGoal` again; hydrate does not restart the loop.
73
+ */
74
+ hydrate(state) {
75
+ this.state = state;
76
+ this.mode = "idle";
77
+ }
78
+ async setGoal(objective, options = {}) {
79
+ if (typeof objective !== "string" || objective.trim() === "") {
80
+ throw new Error("setGoal: objective must be a non-empty string");
81
+ }
82
+ if (objective.length > GOAL_OBJECTIVE_MAX) {
83
+ throw new RangeError(`setGoal: objective exceeds ${GOAL_OBJECTIVE_MAX} chars (got ${objective.length}); point the goal at a file instead`);
84
+ }
85
+ // Replace any active goal — emit a synthetic `cleared` for the old one.
86
+ if (this.state && !isTerminalGoalStatus(this.state.status)) {
87
+ this.transition({
88
+ objective: this.state.objective,
89
+ status: "cleared",
90
+ met: false,
91
+ enforced: this.state.enforced,
92
+ source: "host",
93
+ });
94
+ }
95
+ this.generation++;
96
+ const gen = this.generation;
97
+ this.iterations = 0;
98
+ this.maxIterations = options.maxIterations ?? 12;
99
+ this.sentinel = null;
100
+ this.suspendNext = false;
101
+ const enforce = options.enforce ?? "provider";
102
+ const cap = this.deps.capability;
103
+ const customSentinel = options.sentinel;
104
+ // --- advisory: record only, never gate ---
105
+ if (enforce === "advisory") {
106
+ this.mode = "advisory";
107
+ // Best-effort seed native state so a model-tools provider still "knows"
108
+ // the goal, but without any enforcement loop.
109
+ let nativeSeeded = false;
110
+ if (cap?.mechanism === "model-tools" && this.deps.armNative) {
111
+ nativeSeeded = await safeBool(this.deps.armNative(objective));
112
+ }
113
+ if (gen !== this.generation)
114
+ return { armed: false, mechanism: "emulated" };
115
+ if (nativeSeeded) {
116
+ // The native provider will emit its own `active` goal_status; set
117
+ // optimistic state WITHOUT dispatching (mirrors native mode) so the host
118
+ // doesn't see a duplicate `active`.
119
+ this.state = {
120
+ objective,
121
+ status: "active",
122
+ met: false,
123
+ enforced: false,
124
+ source: "host",
125
+ updatedAt: nowIso(),
126
+ };
127
+ }
128
+ else {
129
+ this.transition({
130
+ objective,
131
+ status: "active",
132
+ met: false,
133
+ enforced: false,
134
+ source: "host",
135
+ });
136
+ }
137
+ return { armed: true, mechanism: cap?.mechanism ?? "emulated" };
138
+ }
139
+ // --- native passthrough (default, no custom sentinel) ---
140
+ const wantNative = enforce === "provider" && !customSentinel && !!this.deps.armNative;
141
+ if (wantNative) {
142
+ const armed = await safeBool(this.deps.armNative(objective));
143
+ if (gen !== this.generation)
144
+ return { armed: false, mechanism: "emulated" };
145
+ if (armed) {
146
+ this.mode = "native";
147
+ // Native providers emit goal_status through their parser; set optimistic
148
+ // state for immediate getGoal() and let `observe` reconcile. Do NOT
149
+ // dispatch here (the parser is the sole emitter in native mode).
150
+ this.state = {
151
+ objective,
152
+ status: "active",
153
+ met: false,
154
+ enforced: cap?.enforced ?? true,
155
+ source: "host",
156
+ updatedAt: nowIso(),
157
+ };
158
+ return {
159
+ armed: true,
160
+ mechanism: cap?.mechanism === "model-tools" ? "model-tools" : "sentinel",
161
+ };
162
+ }
163
+ // Native arm failed → fall through to emulation.
164
+ }
165
+ // --- emulation (forced, custom sentinel, or native fallback) ---
166
+ this.mode = "emulate";
167
+ this.sentinel = customSentinel ?? this.makeDefaultSentinel();
168
+ this.transition({
169
+ objective,
170
+ status: "active",
171
+ met: false,
172
+ enforced: true,
173
+ source: "agentex",
174
+ });
175
+ this.kickoff(objective, gen);
176
+ return { armed: true, mechanism: "emulated" };
177
+ }
178
+ async clearGoal(options = {}) {
179
+ if (!this.state || isTerminalGoalStatus(this.state.status)) {
180
+ return { cleared: false };
181
+ }
182
+ const reason = options.reason ?? "cleared";
183
+ const objective = this.state.objective;
184
+ const enforced = this.state.enforced;
185
+ const wasNative = this.mode === "native";
186
+ this.generation++; // cancel any in-flight emulation loop
187
+ this.mode = "idle";
188
+ this.sentinel = null;
189
+ if (wasNative && this.deps.clearNative) {
190
+ await this.deps.clearNative(reason).catch(() => { });
191
+ if (reason === "blocked") {
192
+ // Native providers (e.g. Codex) have no host-asserted "blocked" — their
193
+ // clear emits a `cleared` notification. Emit `blocked` synthetically so
194
+ // the host sees the intended terminal state; the observe terminal-guard
195
+ // then ignores the provider's trailing `cleared`.
196
+ this.transition({
197
+ objective,
198
+ status: "blocked",
199
+ met: false,
200
+ enforced,
201
+ source: "host",
202
+ blockedReason: "needs_input",
203
+ });
204
+ }
205
+ else {
206
+ // Cleared: rely on the provider's authoritative `cleared` notification;
207
+ // set optimistic state (no dispatch) so getGoal() is immediate.
208
+ this.state = {
209
+ objective,
210
+ status: "cleared",
211
+ met: false,
212
+ enforced,
213
+ source: "host",
214
+ updatedAt: nowIso(),
215
+ };
216
+ }
217
+ return { cleared: true };
218
+ }
219
+ const fields = {
220
+ objective,
221
+ status: reason === "blocked" ? "blocked" : "cleared",
222
+ met: false,
223
+ enforced,
224
+ source: "host",
225
+ };
226
+ if (reason === "blocked")
227
+ fields.blockedReason = "needs_input";
228
+ this.transition(fields);
229
+ return { cleared: true };
230
+ }
231
+ /**
232
+ * Called by the session for every normalized event it dispatches to the host.
233
+ * In native mode the parser is the source of truth for goal_status; adopt it.
234
+ */
235
+ observe(event) {
236
+ if (event.type !== "goal_status")
237
+ return;
238
+ // Once a goal is terminal, suppress stale late events FOR THE SAME goal (a
239
+ // clear-time `active`, or a `cleared` after a host-driven `blocked`) so they
240
+ // can't resurrect/downgrade it — but accept a genuinely NEW goal (a
241
+ // different, non-empty objective, e.g. the model created another).
242
+ if (this.state && isTerminalGoalStatus(this.state.status)) {
243
+ const isNewGoal = !!event.objective && event.objective !== this.state.objective;
244
+ if (!isNewGoal)
245
+ return;
246
+ }
247
+ let next = goalStateFromEvent(event);
248
+ // Defensive: a provider's terminal event (e.g. Codex `thread/goal/cleared`)
249
+ // may omit the objective. Don't let an empty objective erase the known one.
250
+ if (!next.objective && this.state?.objective) {
251
+ next = { ...next, objective: this.state.objective };
252
+ }
253
+ this.state = next;
254
+ if (isTerminalGoalStatus(next.status)) {
255
+ this.mode = "idle";
256
+ this.sentinel = null;
257
+ }
258
+ }
259
+ /**
260
+ * Called by the session whenever a turn settles. Only the emulation engine
261
+ * acts here; native/advisory goals are driven by provider events.
262
+ */
263
+ async onTurnSettled(turn) {
264
+ if (this.mode !== "emulate")
265
+ return;
266
+ if (this.evaluating)
267
+ return; // ignore the default sentinel's meta-turn + nested settles
268
+ if (this.suspendNext) {
269
+ this.suspendNext = false;
270
+ return;
271
+ } // interrupted → pause one turn
272
+ if (turn.status !== "completed")
273
+ return; // aborted/timeout/failed/max_* → don't advance
274
+ if (!this.state || isTerminalGoalStatus(this.state.status))
275
+ return;
276
+ if (!this.sentinel)
277
+ return;
278
+ const gen = this.generation;
279
+ const objective = this.state.objective;
280
+ this.evaluating = true;
281
+ try {
282
+ const verdict = await runSentinel(this.sentinel, {
283
+ objective,
284
+ lastTurn: turn,
285
+ transcriptPath: this.deps.getTranscriptPath?.() ?? null,
286
+ iterations: this.iterations,
287
+ });
288
+ if (gen !== this.generation)
289
+ return; // goal replaced/cleared during eval
290
+ if (verdict.met) {
291
+ this.transition({
292
+ objective,
293
+ status: "met",
294
+ met: true,
295
+ enforced: true,
296
+ source: "sentinel",
297
+ });
298
+ this.mode = "idle";
299
+ this.sentinel = null;
300
+ return;
301
+ }
302
+ this.iterations++;
303
+ if (this.iterations >= this.maxIterations) {
304
+ this.transition({
305
+ objective,
306
+ status: "blocked",
307
+ met: false,
308
+ enforced: true,
309
+ source: "agentex",
310
+ blockedReason: "max_iterations",
311
+ });
312
+ this.mode = "idle";
313
+ this.sentinel = null;
314
+ return;
315
+ }
316
+ // Drive one continuation turn. Don't await its result — its own settle
317
+ // re-enters onTurnSettled and continues the loop.
318
+ const nudge = verdict.nudge ?? defaultNudge(objective, this.iterations);
319
+ void this.deps.send(nudge).catch(() => { });
320
+ }
321
+ finally {
322
+ this.evaluating = false;
323
+ }
324
+ }
325
+ // ---- internals ----
326
+ kickoff(objective, gen) {
327
+ if (gen !== this.generation)
328
+ return;
329
+ void this.deps.send(buildKickoffMessage(objective)).catch(() => { });
330
+ }
331
+ makeDefaultSentinel() {
332
+ return createDefaultSentinel({
333
+ metaSend: async (message) => {
334
+ const handle = await this.deps.send(message);
335
+ return handle.result;
336
+ },
337
+ });
338
+ }
339
+ /** Build, record, and dispatch a synthetic goal_status transition. */
340
+ transition(fields) {
341
+ const event = this.buildEvent(fields);
342
+ this.state = goalStateFromEvent(event);
343
+ this.deps.dispatch(event);
344
+ }
345
+ buildEvent(fields) {
346
+ const event = {
347
+ type: "goal_status",
348
+ objective: fields.objective,
349
+ status: fields.status,
350
+ met: fields.met,
351
+ enforced: fields.enforced,
352
+ source: fields.source,
353
+ timestamp: nowIso(),
354
+ providerType: this.deps.providerType,
355
+ sessionId: this.deps.getSessionId(),
356
+ messageId: null,
357
+ eventId: null,
358
+ turnId: null,
359
+ parentToolCallId: null,
360
+ raw: { synthetic: "goal", ...fields },
361
+ };
362
+ if (fields.blockedReason !== undefined)
363
+ event.blockedReason = fields.blockedReason;
364
+ if (fields.tokensUsed !== undefined)
365
+ event.tokensUsed = fields.tokensUsed;
366
+ if (fields.timeUsedSeconds !== undefined)
367
+ event.timeUsedSeconds = fields.timeUsedSeconds;
368
+ if (fields.tokenBudget !== undefined)
369
+ event.tokenBudget = fields.tokenBudget;
370
+ if (this.mode === "emulate")
371
+ event.iterations = this.iterations;
372
+ return event;
373
+ }
374
+ }
375
+ //# sourceMappingURL=controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.js","sourceRoot":"","sources":["../../src/goals/controller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { GoalController, EMULATED_GOAL_CAPABILITY } from "./controller.js";
2
+ export type { GoalControllerDeps } from "./controller.js";
3
+ export { GOAL_OBJECTIVE_MAX, CODEX_GOAL_TOOLS, isTerminalGoalStatus, normalizeClaudeGoalAttachment, normalizeCodexGoalStatus, normalizeCodexGoalRecord, goalStateFromEvent, latestGoalFromEvents, } from "./normalize.js";
4
+ export type { GoalStatusEvent, NormalizedGoalFields } from "./normalize.js";
5
+ export { runSentinel, createDefaultSentinel, buildKickoffMessage, buildAssessmentPrompt, parseAssessment, defaultNudge, } from "./sentinel.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/goals/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,YAAY,GACb,MAAM,eAAe,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { GoalController, EMULATED_GOAL_CAPABILITY } from "./controller.js";
2
+ export { GOAL_OBJECTIVE_MAX, CODEX_GOAL_TOOLS, isTerminalGoalStatus, normalizeClaudeGoalAttachment, normalizeCodexGoalStatus, normalizeCodexGoalRecord, goalStateFromEvent, latestGoalFromEvents, } from "./normalize.js";
3
+ export { runSentinel, createDefaultSentinel, buildKickoffMessage, buildAssessmentPrompt, parseAssessment, defaultNudge, } from "./sentinel.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/goals/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAE3E,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,6BAA6B,EAC7B,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,WAAW,EACX,qBAAqB,EACrB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,YAAY,GACb,MAAM,eAAe,CAAC"}
@@ -0,0 +1,72 @@
1
+ import type { GoalBlockedReason, GoalSource, GoalState, GoalStatus, StreamEvent } from "../types.js";
2
+ /** Max objective length, matching both native providers (Claude + Codex). */
3
+ export declare const GOAL_OBJECTIVE_MAX = 4000;
4
+ /** The `goal_status` member of the StreamEvent union. */
5
+ export type GoalStatusEvent = Extract<StreamEvent, {
6
+ type: "goal_status";
7
+ }>;
8
+ /**
9
+ * Codex's goal-management tool names. These surface as ordinary
10
+ * `tool_call`/`tool_result` events, NOT as `goal_status` — goal state is keyed
11
+ * off the authoritative `thread_goal_updated` notification instead. This Set is
12
+ * a host-facing recognizer for code that wants to spot the model managing its
13
+ * goal from the tool stream; the library does not consume it internally.
14
+ */
15
+ export declare const CODEX_GOAL_TOOLS: Set<string>;
16
+ /**
17
+ * Normalized goal fields — everything a `goal_status` event needs except the
18
+ * `BaseStreamEventFields` envelope. Parsers fill the envelope; the controller
19
+ * fills it for synthetic (emulation) transitions.
20
+ */
21
+ export interface NormalizedGoalFields {
22
+ objective: string;
23
+ status: GoalStatus;
24
+ met: boolean;
25
+ enforced: boolean;
26
+ source: GoalSource;
27
+ blockedReason?: GoalBlockedReason;
28
+ tokensUsed?: number;
29
+ timeUsedSeconds?: number;
30
+ tokenBudget?: number;
31
+ }
32
+ /** A goal status is terminal when no further work happens against it. */
33
+ export declare function isTerminalGoalStatus(status: GoalStatus): boolean;
34
+ /**
35
+ * Normalize a Claude `goal_status` attachment
36
+ * (`{type:"goal_status", met, sentinel, condition}`) into goal fields. Returns
37
+ * null when the object isn't a goal_status attachment. Claude goals are always
38
+ * sentinel-enforced and binary (`met:false → active`, `met:true → met`).
39
+ */
40
+ export declare function normalizeClaudeGoalAttachment(attachment: Record<string, unknown> | null | undefined): NormalizedGoalFields | null;
41
+ /**
42
+ * Map a raw Codex goal status string into the normalized ladder. Codex's
43
+ * documented statuses are `active|paused|complete|budget-limited`; we also
44
+ * tolerate the reverse-engineered/aliased spellings (`completed`, `achieved`,
45
+ * `budget_limited`, `budgetLimited`, `pursuing`, `blocked`, `cleared`) so the
46
+ * parser survives wire drift.
47
+ */
48
+ export declare function normalizeCodexGoalStatus(raw: string | null | undefined): {
49
+ status: GoalStatus;
50
+ met: boolean;
51
+ blockedReason?: GoalBlockedReason;
52
+ };
53
+ /**
54
+ * Normalize a Codex goal record (`{objective, status, tokensUsed?,
55
+ * timeUsedSeconds?, tokenBudget?}`) — as carried by `thread_goal_updated` /
56
+ * `thread/goal/updated` notifications or `get_goal`/`update_goal` tool output —
57
+ * into goal fields. Codex goals are advisory (`enforced:false`). `source`
58
+ * reflects who drove the transition: the model can only ever write `active`
59
+ * (create) and `complete` (update); other statuses come from user/system.
60
+ */
61
+ export declare function normalizeCodexGoalRecord(goal: Record<string, unknown> | null | undefined, source?: GoalSource): NormalizedGoalFields | null;
62
+ /**
63
+ * Fold a sequence of events into the latest goal state — the building block for
64
+ * resume. Pass the events read back from a transcript (`provider.transcript.read`)
65
+ * or any StreamEvent stream; the most recent `goal_status` wins. Returns null
66
+ * when no goal was ever set (or the goal's last state was terminal and you want
67
+ * to treat that as "no active goal", which the caller decides via `.status`).
68
+ */
69
+ export declare function latestGoalFromEvents(events: Iterable<StreamEvent>): GoalState | null;
70
+ /** Project a `goal_status` stream event back into the durable `GoalState`. */
71
+ export declare function goalStateFromEvent(event: GoalStatusEvent): GoalState;
72
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/goals/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,aAAa,CAAC;AAErB,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,CAAC;AAE5E;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,aAAsD,CAAC;AAEpF;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,UAAU,CAAC;IACnB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAEhE;AAMD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GACrD,oBAAoB,GAAG,IAAI,CAW7B;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG;IACxE,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,OAAO,CAAC;IACb,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACnC,CAqBA;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,EAChD,MAAM,GAAE,UAAoB,GAC3B,oBAAoB,GAAG,IAAI,CAwB7B;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,SAAS,GAAG,IAAI,CAMpF;AAED,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,CAepE"}