@agentex/agent 0.0.22 → 0.0.24
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.
- package/README.md +58 -0
- package/dist/goals/controller.d.ts +106 -0
- package/dist/goals/controller.d.ts.map +1 -0
- package/dist/goals/controller.js +375 -0
- package/dist/goals/controller.js.map +1 -0
- package/dist/goals/index.d.ts +6 -0
- package/dist/goals/index.d.ts.map +1 -0
- package/dist/goals/index.js +4 -0
- package/dist/goals/index.js.map +1 -0
- package/dist/goals/normalize.d.ts +72 -0
- package/dist/goals/normalize.d.ts.map +1 -0
- package/dist/goals/normalize.js +138 -0
- package/dist/goals/normalize.js.map +1 -0
- package/dist/goals/sentinel.d.ts +39 -0
- package/dist/goals/sentinel.d.ts.map +1 -0
- package/dist/goals/sentinel.js +77 -0
- package/dist/goals/sentinel.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/acp/session.d.ts.map +1 -1
- package/dist/providers/acp/session.js +25 -0
- package/dist/providers/acp/session.js.map +1 -1
- package/dist/providers/claude/execute.d.ts.map +1 -1
- package/dist/providers/claude/execute.js +17 -2
- package/dist/providers/claude/execute.js.map +1 -1
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +2 -1
- package/dist/providers/claude/index.js.map +1 -1
- package/dist/providers/claude/parse.d.ts.map +1 -1
- package/dist/providers/claude/parse.js +25 -0
- package/dist/providers/claude/parse.js.map +1 -1
- package/dist/providers/claude/session.d.ts +36 -1
- package/dist/providers/claude/session.d.ts.map +1 -1
- package/dist/providers/claude/session.js +197 -2
- package/dist/providers/claude/session.js.map +1 -1
- package/dist/providers/codex/execute.d.ts.map +1 -1
- package/dist/providers/codex/execute.js +17 -3
- package/dist/providers/codex/execute.js.map +1 -1
- package/dist/providers/codex/index.d.ts.map +1 -1
- package/dist/providers/codex/index.js +2 -1
- package/dist/providers/codex/index.js.map +1 -1
- package/dist/providers/codex/parse.d.ts.map +1 -1
- package/dist/providers/codex/parse.js +74 -3
- package/dist/providers/codex/parse.js.map +1 -1
- package/dist/providers/codex/session.d.ts +30 -1
- package/dist/providers/codex/session.d.ts.map +1 -1
- package/dist/providers/codex/session.js +175 -6
- package/dist/providers/codex/session.js.map +1 -1
- package/dist/providers/opencode/http-session.d.ts.map +1 -1
- package/dist/providers/opencode/http-session.js +25 -0
- package/dist/providers/opencode/http-session.js.map +1 -1
- package/dist/providers/opencode/index.d.ts.map +1 -1
- package/dist/providers/opencode/index.js +2 -0
- package/dist/providers/opencode/index.js.map +1 -1
- package/dist/providers/pi/index.d.ts.map +1 -1
- package/dist/providers/pi/index.js +2 -0
- package/dist/providers/pi/index.js.map +1 -1
- package/dist/providers/pi/session.d.ts +8 -1
- package/dist/providers/pi/session.d.ts.map +1 -1
- package/dist/providers/pi/session.js +28 -2
- package/dist/providers/pi/session.js.map +1 -1
- package/dist/types.d.ts +249 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/endpoint.d.ts +38 -0
- package/dist/utils/endpoint.d.ts.map +1 -0
- package/dist/utils/endpoint.js +151 -0
- package/dist/utils/endpoint.js.map +1 -0
- package/dist/utils/env.d.ts.map +1 -1
- package/dist/utils/env.js +5 -1
- package/dist/utils/env.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,6 +100,64 @@ loadProvidersFromConfig({
|
|
|
100
100
|
});
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
**Per-call endpoint (`config.endpoint`).** When the endpoint is chosen per
|
|
104
|
+
session rather than registered up front — e.g. an app that lets each user bring
|
|
105
|
+
their own model — set `endpoint` on `ProviderConfig`. The library keeps no state
|
|
106
|
+
and stores no config file; you pass the endpoint on the call and it is
|
|
107
|
+
translated into whatever the provider's CLI understands, at spawn:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const session = await getProvider("claude").createSession({
|
|
111
|
+
cwd,
|
|
112
|
+
config: {
|
|
113
|
+
model: "sonnet", // tier alias still works — see modelMap
|
|
114
|
+
endpoint: {
|
|
115
|
+
baseUrl: "https://api.z.ai/api/anthropic",
|
|
116
|
+
authToken: "…", // → Authorization: Bearer (or `apiKey` → x-api-key)
|
|
117
|
+
modelMap: { sonnet: "glm-5.1" }, // alias → concrete id on the endpoint
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Translation is per provider (there is no shared wire format):
|
|
124
|
+
|
|
125
|
+
| Provider | `baseUrl` / auth | `modelMap` |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| `claude` | `ANTHROPIC_BASE_URL` + `ANTHROPIC_AUTH_TOKEN`/`ANTHROPIC_API_KEY` (`headers` → `ANTHROPIC_CUSTOM_HEADERS`) | → `ANTHROPIC_DEFAULT_{OPUS,SONNET,HAIKU,FABLE}_MODEL` |
|
|
128
|
+
| `codex` | synthesized `[model_providers.custom]` block (`base_url`, `wire_api="responses"`, `env_key`) via `-c` overrides; key injected into env. Requires `baseUrl`. | ignored — Codex has no tier aliases; pass a concrete `model` |
|
|
129
|
+
| others | ignored (like `allowedTools` on unsupported providers) | — |
|
|
130
|
+
|
|
131
|
+
`endpoint` is applied once at spawn, so it is per-session / per-`exec`: change it
|
|
132
|
+
by starting a fresh `createSession`/`exec` (resume re-applies it), never
|
|
133
|
+
mid-session. Set exactly one of `authToken` / `apiKey`.
|
|
134
|
+
|
|
135
|
+
**Credential hygiene (claude).** When a custom `baseUrl` is set, only the auth in
|
|
136
|
+
`endpoint` reaches it — ambient `ANTHROPIC_API_KEY`/`ANTHROPIC_AUTH_TOKEN` from the
|
|
137
|
+
host env is not forwarded to the third party, and ambient alternate-routing config
|
|
138
|
+
(Bedrock/Vertex/Foundry) is cleared so it can't steer Claude off the endpoint.
|
|
139
|
+
Pass auth explicitly. One consequence: an observability-proxy pattern (Helicone /
|
|
140
|
+
LangSmith-style, where the proxy forwards using your *own* Anthropic key) no longer
|
|
141
|
+
picks up the ambient key — pass it deliberately, e.g.
|
|
142
|
+
`endpoint: { baseUrl, apiKey: process.env.ANTHROPIC_API_KEY }`.
|
|
143
|
+
|
|
144
|
+
**Codex header safety.** Codex header values are passed to the child via env
|
|
145
|
+
(`env_http_headers`), never argv, so a secret header (`Authorization`, `X-API-Key`)
|
|
146
|
+
doesn't leak through `ps`. Header *names* must be TOML bare keys.
|
|
147
|
+
|
|
148
|
+
**Codex speaks the Responses API.** Codex removed the Chat Completions
|
|
149
|
+
(`wire_api="chat"`) protocol in Feb 2026, so a custom endpoint must implement the
|
|
150
|
+
OpenAI Responses API, directly or via a translating gateway (e.g. LiteLLM). Pure
|
|
151
|
+
chat-completions endpoints won't work through Codex. If you're pinned to a
|
|
152
|
+
pre-Feb-2026 Codex that still needs `chat`, override it on the one-shot `exec`
|
|
153
|
+
path with `extraArgs: ["-c", 'model_providers.custom.wire_api="chat"']`.
|
|
154
|
+
|
|
155
|
+
Prefer derived providers when the same endpoint is reused across many calls (and
|
|
156
|
+
for heavy Codex config customization); prefer `config.endpoint` when it varies
|
|
157
|
+
per session and the host owns storage. To override the synthesized Codex config,
|
|
158
|
+
`extraArgs` is reliable on the one-shot `exec` path (it follows the endpoint
|
|
159
|
+
`-c` flags); for sessions, a derived provider is the robust path.
|
|
160
|
+
|
|
103
161
|
**ACP agents.** Any agent speaking the [Agent Client Protocol](https://agentclientprotocol.com)
|
|
104
162
|
is a few lines:
|
|
105
163
|
|
|
@@ -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
|