@graphorin/core 0.6.0 → 0.7.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.
- package/CHANGELOG.md +88 -0
- package/README.md +14 -8
- package/dist/channels/channels.d.ts.map +1 -1
- package/dist/channels/index.d.ts +2 -2
- package/dist/channels/index.js +2 -2
- package/dist/channels/pause.d.ts +47 -2
- package/dist/channels/pause.d.ts.map +1 -1
- package/dist/channels/pause.js +62 -2
- package/dist/channels/pause.js.map +1 -1
- package/dist/contracts/checkpoint-store.d.ts +97 -1
- package/dist/contracts/checkpoint-store.d.ts.map +1 -1
- package/dist/contracts/checkpoint-store.js.map +1 -1
- package/dist/contracts/index.d.ts +5 -5
- package/dist/contracts/index.js +2 -1
- package/dist/contracts/memory-store.d.ts +59 -2
- package/dist/contracts/memory-store.d.ts.map +1 -1
- package/dist/contracts/provider.d.ts +20 -6
- package/dist/contracts/provider.d.ts.map +1 -1
- package/dist/contracts/session-store.d.ts +10 -2
- package/dist/contracts/session-store.d.ts.map +1 -1
- package/dist/contracts/tool.d.ts +75 -1
- package/dist/contracts/tool.d.ts.map +1 -1
- package/dist/contracts/tool.js +57 -0
- package/dist/contracts/tool.js.map +1 -0
- package/dist/contracts/tracer.d.ts +53 -5
- package/dist/contracts/tracer.d.ts.map +1 -1
- package/dist/contracts/tracer.js.map +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/types/agent-event-wire.d.ts +74 -0
- package/dist/types/agent-event-wire.d.ts.map +1 -0
- package/dist/types/agent-event-wire.js +130 -0
- package/dist/types/agent-event-wire.js.map +1 -0
- package/dist/types/agent-event.d.ts +48 -6
- package/dist/types/agent-event.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.js +2 -1
- package/dist/types/memory.d.ts +11 -0
- package/dist/types/memory.d.ts.map +1 -1
- package/dist/types/run.d.ts +86 -4
- package/dist/types/run.d.ts.map +1 -1
- package/dist/types/run.js.map +1 -1
- package/dist/types/tool.d.ts +10 -0
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/types/usage.d.ts +11 -1
- package/dist/types/usage.d.ts.map +1 -1
- package/dist/types/usage.js.map +1 -1
- package/dist/utils/binary-json.d.ts +165 -0
- package/dist/utils/binary-json.d.ts.map +1 -0
- package/dist/utils/binary-json.js +240 -0
- package/dist/utils/binary-json.js.map +1 -0
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/index.js +2 -1
- package/dist/utils/validation.d.ts +10 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +1 -1
- package/dist/utils/validation.js.map +1 -1
- package/package.json +9 -7
- package/src/channels/channels.ts +206 -0
- package/src/channels/directive.ts +41 -0
- package/src/channels/dispatch.ts +37 -0
- package/src/channels/durable.ts +151 -0
- package/src/channels/index.ts +62 -0
- package/src/channels/pause.ts +216 -0
- package/src/contracts/auth-token-store.ts +42 -0
- package/src/contracts/checkpoint-store.ts +256 -0
- package/src/contracts/embedder.ts +42 -0
- package/src/contracts/eval-scorer.ts +44 -0
- package/src/contracts/index.ts +112 -0
- package/src/contracts/local-provider-trust.ts +33 -0
- package/src/contracts/logger.ts +61 -0
- package/src/contracts/memory-store.ts +187 -0
- package/src/contracts/oauth-server-store.ts +78 -0
- package/src/contracts/preferred-model.ts +56 -0
- package/src/contracts/provider.ts +316 -0
- package/src/contracts/reasoning-retention.ts +52 -0
- package/src/contracts/redaction-validator.ts +56 -0
- package/src/contracts/sandbox.ts +70 -0
- package/src/contracts/secret-ref.ts +22 -0
- package/src/contracts/secret-value.ts +117 -0
- package/src/contracts/secrets-store.ts +90 -0
- package/src/contracts/session-store.ts +163 -0
- package/src/contracts/token-counter.ts +23 -0
- package/src/contracts/tool.ts +397 -0
- package/src/contracts/tracer.ts +219 -0
- package/src/contracts/trigger-store.ts +40 -0
- package/src/index.ts +23 -0
- package/src/types/agent-event-wire.ts +193 -0
- package/src/types/agent-event.ts +579 -0
- package/src/types/handoff.ts +111 -0
- package/src/types/index.ts +148 -0
- package/src/types/memory.ts +427 -0
- package/src/types/message.ts +174 -0
- package/src/types/run.ts +312 -0
- package/src/types/sensitivity.ts +35 -0
- package/src/types/session-scope.ts +18 -0
- package/src/types/stop-condition.ts +108 -0
- package/src/types/tool-call.ts +24 -0
- package/src/types/tool.ts +324 -0
- package/src/types/usage.ts +120 -0
- package/src/types/workflow-event.ts +132 -0
- package/src/utils/assert-never.ts +24 -0
- package/src/utils/async-context.ts +55 -0
- package/src/utils/binary-json.ts +425 -0
- package/src/utils/hash.ts +122 -0
- package/src/utils/index.ts +57 -0
- package/src/utils/streams.ts +233 -0
- package/src/utils/validation.ts +82 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { ToolCall } from './tool-call.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A single multimodal content part attached to a chat-style message.
|
|
5
|
+
*
|
|
6
|
+
* The discriminated union is exhaustive: every variant carries a literal
|
|
7
|
+
* `type` field used by both the runtime and the type system to pick the
|
|
8
|
+
* branch. New variants must be added to all three of: this union, every
|
|
9
|
+
* `assertNever` switch in the codebase, and the wire-stable adapters.
|
|
10
|
+
*
|
|
11
|
+
* @stable
|
|
12
|
+
*/
|
|
13
|
+
export type MessageContent =
|
|
14
|
+
| TextContent
|
|
15
|
+
| ImageContent
|
|
16
|
+
| AudioContent
|
|
17
|
+
| FileContent
|
|
18
|
+
| ReasoningContent;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Plain UTF-8 text part. The default for textual replies and tool I/O.
|
|
22
|
+
*
|
|
23
|
+
* @stable
|
|
24
|
+
*/
|
|
25
|
+
export interface TextContent {
|
|
26
|
+
readonly type: 'text';
|
|
27
|
+
readonly text: string;
|
|
28
|
+
/**
|
|
29
|
+
* Optional opaque trace of the agent-runtime decisions that produced
|
|
30
|
+
* this content part. Bounded-length, no PII, no secret values.
|
|
31
|
+
* Round-tripped bytes-equal through `Session.push / list / export /
|
|
32
|
+
* import`. See `@graphorin/sessions` commentary-phase sanitization.
|
|
33
|
+
*/
|
|
34
|
+
readonly causalityChain?: ReadonlyArray<string>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Image attachment. The `image` field accepts either raw bytes or a `URL`
|
|
39
|
+
* - adapters dereference the URL when the provider only accepts inline
|
|
40
|
+
* payloads.
|
|
41
|
+
*
|
|
42
|
+
* @stable
|
|
43
|
+
*/
|
|
44
|
+
export interface ImageContent {
|
|
45
|
+
readonly type: 'image';
|
|
46
|
+
readonly image: Uint8Array | URL;
|
|
47
|
+
readonly mimeType?: string;
|
|
48
|
+
/** See {@link TextContent.causalityChain}. */
|
|
49
|
+
readonly causalityChain?: ReadonlyArray<string>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Audio attachment (e.g. voice messages). Note: voice realtime / TTS / STT
|
|
54
|
+
* are out of scope for v0.1 - these messages are static blobs.
|
|
55
|
+
*
|
|
56
|
+
* @stable
|
|
57
|
+
*/
|
|
58
|
+
export interface AudioContent {
|
|
59
|
+
readonly type: 'audio';
|
|
60
|
+
readonly audio: Uint8Array | URL;
|
|
61
|
+
readonly mimeType?: string;
|
|
62
|
+
/** See {@link TextContent.causalityChain}. */
|
|
63
|
+
readonly causalityChain?: ReadonlyArray<string>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generic file attachment (PDF, CSV, …). `mimeType` is mandatory because
|
|
68
|
+
* many providers gate file ingestion on it.
|
|
69
|
+
*
|
|
70
|
+
* @stable
|
|
71
|
+
*/
|
|
72
|
+
export interface FileContent {
|
|
73
|
+
readonly type: 'file';
|
|
74
|
+
readonly file: Uint8Array | URL;
|
|
75
|
+
readonly mimeType: string;
|
|
76
|
+
readonly filename?: string;
|
|
77
|
+
/** See {@link TextContent.causalityChain}. */
|
|
78
|
+
readonly causalityChain?: ReadonlyArray<string>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Reasoning content emitted by reasoning-capable models. Stored
|
|
83
|
+
* separately from `text` so that consumers can choose to hide / strip
|
|
84
|
+
* it (per the streaming-first principle and the replay-redaction
|
|
85
|
+
* policy).
|
|
86
|
+
*
|
|
87
|
+
* @stable
|
|
88
|
+
*/
|
|
89
|
+
export interface ReasoningContent {
|
|
90
|
+
readonly type: 'reasoning';
|
|
91
|
+
readonly text: string;
|
|
92
|
+
/**
|
|
93
|
+
* Provider-specific opaque metadata that MUST round-trip byte-equal
|
|
94
|
+
* when the effective `reasoningRetention` is not `'strip'`. The
|
|
95
|
+
* field is provider-supplied protocol payload, not user content,
|
|
96
|
+
* and is therefore exempt from prompt-redaction scanning.
|
|
97
|
+
*
|
|
98
|
+
* Anthropic Claude tool-use thinking blocks supply
|
|
99
|
+
* `{ provider: 'anthropic', signature, data? }`; other providers
|
|
100
|
+
* are free to populate whatever opaque keys their wire contract
|
|
101
|
+
* requires. Adapters that do not need round-tripping omit this
|
|
102
|
+
* field entirely.
|
|
103
|
+
*/
|
|
104
|
+
readonly meta?: ReasoningContentMeta;
|
|
105
|
+
/** See {@link TextContent.causalityChain}. */
|
|
106
|
+
readonly causalityChain?: ReadonlyArray<string>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Opaque metadata round-tripped on `ReasoningContent`. Adapter-defined
|
|
111
|
+
* keys; consumers must NOT introspect or modify the contents.
|
|
112
|
+
*
|
|
113
|
+
* @stable
|
|
114
|
+
*/
|
|
115
|
+
export interface ReasoningContentMeta {
|
|
116
|
+
readonly provider?: string;
|
|
117
|
+
readonly signature?: string;
|
|
118
|
+
readonly data?: string;
|
|
119
|
+
readonly [extraKey: string]: unknown;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Logical role of a message in a conversation.
|
|
124
|
+
*
|
|
125
|
+
* @stable
|
|
126
|
+
*/
|
|
127
|
+
export type MessageRole = 'system' | 'user' | 'assistant' | 'tool';
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Chat message. The shape is provider-agnostic: adapters convert it to /
|
|
131
|
+
* from the wire format.
|
|
132
|
+
*
|
|
133
|
+
* - System messages must be plain strings (multimodal system messages are
|
|
134
|
+
* not in the v0.1 surface).
|
|
135
|
+
* - Assistant messages may carry `toolCalls` alongside their content.
|
|
136
|
+
* - Tool messages carry the originating `toolCallId` so the model can
|
|
137
|
+
* correlate the response to its previous request.
|
|
138
|
+
*
|
|
139
|
+
* @stable
|
|
140
|
+
*/
|
|
141
|
+
export type Message = SystemMessage | UserMessage | AssistantMessage | ToolMessage;
|
|
142
|
+
|
|
143
|
+
/** @stable */
|
|
144
|
+
export interface SystemMessage {
|
|
145
|
+
readonly role: 'system';
|
|
146
|
+
readonly content: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** @stable */
|
|
150
|
+
export interface UserMessage {
|
|
151
|
+
readonly role: 'user';
|
|
152
|
+
readonly content: string | readonly MessageContent[];
|
|
153
|
+
/** Multi-agent attribution: which user persona this came from, if any. */
|
|
154
|
+
readonly userId?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @stable */
|
|
158
|
+
export interface AssistantMessage {
|
|
159
|
+
readonly role: 'assistant';
|
|
160
|
+
readonly content: string | readonly MessageContent[];
|
|
161
|
+
readonly toolCalls?: readonly ToolCall[];
|
|
162
|
+
/**
|
|
163
|
+
* Multi-agent attribution: which agent produced this message. Required
|
|
164
|
+
* by the multi-agent crew acceptance criteria.
|
|
165
|
+
*/
|
|
166
|
+
readonly agentId?: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** @stable */
|
|
170
|
+
export interface ToolMessage {
|
|
171
|
+
readonly role: 'tool';
|
|
172
|
+
readonly toolCallId: string;
|
|
173
|
+
readonly content: string | readonly MessageContent[];
|
|
174
|
+
}
|
package/src/types/run.ts
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import type { Tracer } from '../contracts/tracer.js';
|
|
2
|
+
import type { HandoffRecord } from './handoff.js';
|
|
3
|
+
import type { Message } from './message.js';
|
|
4
|
+
import type { CompletedToolCall, ToolApproval } from './tool.js';
|
|
5
|
+
import type { ToolCall } from './tool-call.js';
|
|
6
|
+
import type { ModelUsage, Usage, UsageAccumulator } from './usage.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Status of an in-flight or completed agent run. Append-only persistence
|
|
10
|
+
* stores expose this verbatim on the `runs` table.
|
|
11
|
+
*
|
|
12
|
+
* @stable
|
|
13
|
+
*/
|
|
14
|
+
export type RunStatus = 'running' | 'completed' | 'failed' | 'aborted' | 'awaiting_approval';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Single step inside an agent run. The agent runtime appends one
|
|
18
|
+
* `RunStep` per provider call.
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
export interface RunStep {
|
|
23
|
+
readonly stepNumber: number;
|
|
24
|
+
readonly startedAt: string;
|
|
25
|
+
readonly endedAt?: string;
|
|
26
|
+
readonly usage?: Usage;
|
|
27
|
+
readonly toolCalls: readonly CompletedToolCall[];
|
|
28
|
+
/**
|
|
29
|
+
* Stable agent id active for this step (changes after a handoff).
|
|
30
|
+
*/
|
|
31
|
+
readonly agentId: string;
|
|
32
|
+
/**
|
|
33
|
+
* The model response this step produced, recorded when the agent runs
|
|
34
|
+
* with `recordProviderResponses: true` (C3). Enables deterministic
|
|
35
|
+
* replay: `createReplayProvider(state)` serves these back in order so
|
|
36
|
+
* a run re-executes without live model calls.
|
|
37
|
+
*/
|
|
38
|
+
readonly providerResponse?: RunStepProviderResponse;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Journaled model response for one step (C3, opt-in via the agent's
|
|
43
|
+
* `recordProviderResponses`). Captures the RAW model output - the text
|
|
44
|
+
* before any lateral-leak block replaced it in the transcript - so a
|
|
45
|
+
* replay reproduces the original run faithfully.
|
|
46
|
+
*
|
|
47
|
+
* @stable
|
|
48
|
+
*/
|
|
49
|
+
export interface RunStepProviderResponse {
|
|
50
|
+
readonly modelId: string;
|
|
51
|
+
readonly text?: string;
|
|
52
|
+
readonly toolCalls?: ReadonlyArray<ToolCall>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Per-model usage breakdown carried alongside the aggregate
|
|
57
|
+
* {@link Usage} on {@link RunState}. Populated by the per-step retry
|
|
58
|
+
* loop when `Agent.fallbackModels` fires; for runs that never fall
|
|
59
|
+
* back, the map carries a single entry for the primary model with
|
|
60
|
+
* `attemptCount: 1`.
|
|
61
|
+
*
|
|
62
|
+
* The aggregate `RunState.usage` is always the sum of every entry's
|
|
63
|
+
* `Usage` portion (the field is asserted in tests).
|
|
64
|
+
*
|
|
65
|
+
* @stable
|
|
66
|
+
*/
|
|
67
|
+
export interface RunStateUsageByModel {
|
|
68
|
+
readonly [modelId: string]: Usage & { readonly attemptCount: number };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The full, serializable state of a run. The agent runtime persists this
|
|
73
|
+
* to the checkpoint store on every `awaiting_approval` boundary, so a
|
|
74
|
+
* separate process can resume the run.
|
|
75
|
+
*
|
|
76
|
+
* JSON stability is guaranteed by the serializer, not by naive
|
|
77
|
+
* `JSON.stringify`: `messages` and tool-outcome `contentParts` may carry
|
|
78
|
+
* `Uint8Array | URL` payloads, which the documented wire projection
|
|
79
|
+
* (`WireRunState` via `toJsonSafeRunState`) encodes as base64 / href
|
|
80
|
+
* envelopes before stringification. No `Map`, `Set` or `Date` appears
|
|
81
|
+
* anywhere in the shape.
|
|
82
|
+
*
|
|
83
|
+
* @stable
|
|
84
|
+
*/
|
|
85
|
+
export interface RunState {
|
|
86
|
+
readonly id: string;
|
|
87
|
+
readonly agentId: string;
|
|
88
|
+
/**
|
|
89
|
+
* The agent whose model drives the NEXT step. During a handoff it is
|
|
90
|
+
* the target for exactly the child observation window and is restored
|
|
91
|
+
* to the parent when the child returns (W-034) - the child's identity
|
|
92
|
+
* is durably recorded in {@link RunState.handoffs}, never here.
|
|
93
|
+
*/
|
|
94
|
+
readonly currentAgentId: string;
|
|
95
|
+
readonly sessionId: string;
|
|
96
|
+
readonly userId?: string;
|
|
97
|
+
status: RunStatus;
|
|
98
|
+
readonly steps: RunStep[];
|
|
99
|
+
readonly messages: Message[];
|
|
100
|
+
readonly pendingApprovals: ToolApproval[];
|
|
101
|
+
readonly handoffs: HandoffRecord[];
|
|
102
|
+
readonly usage: Usage;
|
|
103
|
+
/**
|
|
104
|
+
* Per-model usage breakdown. Populated by the per-step retry loop
|
|
105
|
+
* when `Agent.fallbackModels` fires (RB-48 / suggested DEC-164 /
|
|
106
|
+
* suggested ADR-052). Backward-compat: rehydrating a serialized
|
|
107
|
+
* state that omits the field synthesizes a single-entry map for
|
|
108
|
+
* the primary model.
|
|
109
|
+
*/
|
|
110
|
+
usageByModel?: RunStateUsageByModel;
|
|
111
|
+
/**
|
|
112
|
+
* AG-19: coarse data-flow taint summary, carried across suspend/resume so a
|
|
113
|
+
* resumed run does not start with an empty ledger that silently un-gates
|
|
114
|
+
* sinks exposed before the suspend. Structurally matches
|
|
115
|
+
* `@graphorin/security`'s `TaintLedgerSnapshot` (core takes no security
|
|
116
|
+
* dependency); only the load-bearing flags are persisted - never the tracked
|
|
117
|
+
* untrusted text spans.
|
|
118
|
+
*/
|
|
119
|
+
taintSummary?: RunTaintSummary;
|
|
120
|
+
/**
|
|
121
|
+
* AG-19: names of deferred tools promoted by `tool_search` this run, carried
|
|
122
|
+
* across suspend/resume so discovered tools remain in the per-step catalogue.
|
|
123
|
+
*/
|
|
124
|
+
promotedTools?: ReadonlyArray<string>;
|
|
125
|
+
/**
|
|
126
|
+
* D6 structured plan/todo list - the agent's own working plan,
|
|
127
|
+
* journaled so it survives suspend/resume (a TodoWrite-style tool
|
|
128
|
+
* mutates it, and attention-recitation renders it back into the
|
|
129
|
+
* prompt each turn). Absent until the agent writes one.
|
|
130
|
+
*/
|
|
131
|
+
todos?: ReadonlyArray<TodoItem>;
|
|
132
|
+
/**
|
|
133
|
+
* W-001: sub-agent runs parked on this (parent) run because the
|
|
134
|
+
* child suspended with `awaiting_approval`. Each entry snapshots the
|
|
135
|
+
* suspended child state; the child's pending approvals are mirrored
|
|
136
|
+
* onto this run's `pendingApprovals` with `subRunToolCallId` set to
|
|
137
|
+
* the entry's `toolCallId`. Absent until a child parks.
|
|
138
|
+
*/
|
|
139
|
+
pendingSubRuns?: PendingSubRun[];
|
|
140
|
+
readonly startedAt: string;
|
|
141
|
+
finishedAt?: string;
|
|
142
|
+
error?: RunError;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* One sub-agent run parked on its parent because the child suspended
|
|
147
|
+
* awaiting approvals (W-001). `state` is a JSON-compatible snapshot of
|
|
148
|
+
* the suspended child run; in serialized form (the agent package's
|
|
149
|
+
* `SerializedRunState`) it carries the child's own version-stamped,
|
|
150
|
+
* secret-redacted snapshot, recursively.
|
|
151
|
+
*
|
|
152
|
+
* @stable
|
|
153
|
+
*/
|
|
154
|
+
export interface PendingSubRun {
|
|
155
|
+
/** The PARENT's toolCallId of the parked handoff / sub-agent call. */
|
|
156
|
+
readonly toolCallId: string;
|
|
157
|
+
/** The parent-side tool name (`transfer_to_<name>` or the toTool name). */
|
|
158
|
+
readonly toolName: string;
|
|
159
|
+
/** The child agent's configured name (for diagnostics and usage folding). */
|
|
160
|
+
readonly targetAgentName: string;
|
|
161
|
+
/** Suspended child run state (carries the child's own pendingApprovals). */
|
|
162
|
+
readonly state: RunState;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Read-only projection of {@link RunState} handed to tools and hooks
|
|
167
|
+
* via {@link RunContext.state} (W-047). Structurally identical to
|
|
168
|
+
* `RunState` - `RunState` is assignable to it - but every property is
|
|
169
|
+
* `readonly` and every array a `ReadonlyArray`, so typed tool code
|
|
170
|
+
* cannot corrupt run bookkeeping (splice `pendingApprovals`, flip
|
|
171
|
+
* `status`, ...). This is a compile-time contract only: there is no
|
|
172
|
+
* runtime freeze. A hand-written mirror (not a generic DeepReadonly):
|
|
173
|
+
* the nested types are already readonly-typed, and keyof-parity with
|
|
174
|
+
* `RunState` is pinned by type tests.
|
|
175
|
+
*
|
|
176
|
+
* @stable
|
|
177
|
+
*/
|
|
178
|
+
export interface ReadonlyRunState {
|
|
179
|
+
readonly id: string;
|
|
180
|
+
readonly agentId: string;
|
|
181
|
+
readonly currentAgentId: string;
|
|
182
|
+
readonly sessionId: string;
|
|
183
|
+
readonly userId?: string;
|
|
184
|
+
readonly status: RunStatus;
|
|
185
|
+
readonly steps: ReadonlyArray<RunStep>;
|
|
186
|
+
readonly messages: ReadonlyArray<Message>;
|
|
187
|
+
readonly pendingApprovals: ReadonlyArray<ToolApproval>;
|
|
188
|
+
readonly handoffs: ReadonlyArray<HandoffRecord>;
|
|
189
|
+
readonly usage: Usage;
|
|
190
|
+
/** See {@link RunState.usageByModel}. */
|
|
191
|
+
readonly usageByModel?: RunStateUsageByModel;
|
|
192
|
+
/** See {@link RunState.taintSummary}. */
|
|
193
|
+
readonly taintSummary?: RunTaintSummary;
|
|
194
|
+
/** See {@link RunState.promotedTools}. */
|
|
195
|
+
readonly promotedTools?: ReadonlyArray<string>;
|
|
196
|
+
/** See {@link RunState.todos}. */
|
|
197
|
+
readonly todos?: ReadonlyArray<TodoItem>;
|
|
198
|
+
/** See {@link RunState.pendingSubRuns}. */
|
|
199
|
+
readonly pendingSubRuns?: ReadonlyArray<PendingSubRun>;
|
|
200
|
+
readonly startedAt: string;
|
|
201
|
+
readonly finishedAt?: string;
|
|
202
|
+
readonly error?: RunError;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Coarse, serializable data-flow taint summary persisted in {@link RunState}
|
|
207
|
+
* across suspend/resume (AG-19). Structurally identical to
|
|
208
|
+
* `@graphorin/security`'s `TaintLedgerSnapshot`; carries no untrusted text.
|
|
209
|
+
*
|
|
210
|
+
* @stable
|
|
211
|
+
*/
|
|
212
|
+
export interface RunTaintSummary {
|
|
213
|
+
readonly untrustedSeen: boolean;
|
|
214
|
+
readonly sensitiveSeen: boolean;
|
|
215
|
+
readonly untrustedSourceKinds: ReadonlyArray<string>;
|
|
216
|
+
/**
|
|
217
|
+
* C6: one-way FNV-1a hashes of normalized untrusted-span tiles. Re-arms
|
|
218
|
+
* the verbatim-carry probe after a resume at tile granularity. Hashes
|
|
219
|
+
* only - no untrusted text is ever persisted (the invariant above
|
|
220
|
+
* holds).
|
|
221
|
+
*/
|
|
222
|
+
readonly spanTileHashes?: ReadonlyArray<string>;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* One item in the agent's structured plan (D6). `status` drives both
|
|
227
|
+
* the recitation rendering and progress reporting; `id` lets a
|
|
228
|
+
* status-flip mutation target an item without rewriting the list.
|
|
229
|
+
*
|
|
230
|
+
* @stable
|
|
231
|
+
*/
|
|
232
|
+
export interface TodoItem {
|
|
233
|
+
readonly id: string;
|
|
234
|
+
readonly content: string;
|
|
235
|
+
readonly status: 'pending' | 'in_progress' | 'completed';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Snapshot helper used by `@graphorin/observability` aggregators to
|
|
240
|
+
* convert the on-disk `usageByModel` shape into the canonical
|
|
241
|
+
* {@link ModelUsage} array. Pure utility - kept in core so consumers
|
|
242
|
+
* do not have to take an observability dependency just to flatten a
|
|
243
|
+
* run-state breakdown.
|
|
244
|
+
*
|
|
245
|
+
* @stable
|
|
246
|
+
*/
|
|
247
|
+
export function flattenUsageByModel(
|
|
248
|
+
byModel: RunStateUsageByModel | undefined,
|
|
249
|
+
): ReadonlyArray<ModelUsage> {
|
|
250
|
+
if (byModel === undefined) return [];
|
|
251
|
+
const out: ModelUsage[] = [];
|
|
252
|
+
for (const [modelId, entry] of Object.entries(byModel)) {
|
|
253
|
+
const m: ModelUsage = {
|
|
254
|
+
modelId,
|
|
255
|
+
promptTokens: entry.promptTokens,
|
|
256
|
+
completionTokens: entry.completionTokens,
|
|
257
|
+
totalTokens: entry.totalTokens,
|
|
258
|
+
callCount: entry.attemptCount,
|
|
259
|
+
...(entry.reasoningTokens !== undefined ? { reasoningTokens: entry.reasoningTokens } : {}),
|
|
260
|
+
...(entry.cost !== undefined ? { cost: entry.cost } : {}),
|
|
261
|
+
};
|
|
262
|
+
out.push(m);
|
|
263
|
+
}
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Failure carried by `RunState.error`. The shape mirrors the wire format
|
|
269
|
+
* used by `agent.error` events.
|
|
270
|
+
*
|
|
271
|
+
* @stable
|
|
272
|
+
*/
|
|
273
|
+
export interface RunError {
|
|
274
|
+
readonly message: string;
|
|
275
|
+
readonly code: string;
|
|
276
|
+
readonly details?: unknown;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Per-run dependency / context bag handed to every tool, hook and
|
|
281
|
+
* provider middleware in scope. Generic over the user-defined deps shape.
|
|
282
|
+
*
|
|
283
|
+
* `tracer`, `signal`, `usage` and `state` are always present; everything
|
|
284
|
+
* else is optional.
|
|
285
|
+
*
|
|
286
|
+
* @stable
|
|
287
|
+
*/
|
|
288
|
+
export interface RunContext<TDeps = unknown> {
|
|
289
|
+
readonly runId: string;
|
|
290
|
+
readonly sessionId: string;
|
|
291
|
+
readonly userId?: string;
|
|
292
|
+
readonly agentId: string;
|
|
293
|
+
readonly deps: TDeps;
|
|
294
|
+
readonly tracer: Tracer;
|
|
295
|
+
readonly signal: AbortSignal;
|
|
296
|
+
readonly usage: UsageAccumulator;
|
|
297
|
+
readonly stepNumber: number;
|
|
298
|
+
readonly messages: ReadonlyArray<Message>;
|
|
299
|
+
/**
|
|
300
|
+
* Read-only snapshot of the run's state (W-047). Tools observe the
|
|
301
|
+
* run; they do not mutate its bookkeeping - writes to `status`,
|
|
302
|
+
* `pendingApprovals` etc. are compile errors. The runtime keeps the
|
|
303
|
+
* only mutable reference.
|
|
304
|
+
*/
|
|
305
|
+
readonly state: ReadonlyRunState;
|
|
306
|
+
/**
|
|
307
|
+
* C7: the current `agent.step` span (when the runtime traces). Spans
|
|
308
|
+
* created inside tool execution parent under it so a run's traces
|
|
309
|
+
* form one tree.
|
|
310
|
+
*/
|
|
311
|
+
readonly span?: import('../contracts/tracer.js').AISpan;
|
|
312
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sensitivity tier for any piece of data flowing through Graphorin.
|
|
3
|
+
*
|
|
4
|
+
* - `'public'` - non-sensitive content; safe for any provider, any sink.
|
|
5
|
+
* - `'internal'` - default for user-generated content; safe for trusted
|
|
6
|
+
* providers (loopback / private-network) but redacted for public-tls
|
|
7
|
+
* exporters and replay consumers.
|
|
8
|
+
* - `'secret'` - credentials, tokens, encryption keys; only ever stays
|
|
9
|
+
* in-process and is never serialized to the wire by default.
|
|
10
|
+
*
|
|
11
|
+
* @stable
|
|
12
|
+
*/
|
|
13
|
+
export type Sensitivity = 'public' | 'internal' | 'secret';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Strict ordering used by sensitivity-aware filters
|
|
17
|
+
* (`record.sensitivity ⊆ provider.acceptsSensitivity`).
|
|
18
|
+
*
|
|
19
|
+
* @stable
|
|
20
|
+
*/
|
|
21
|
+
export const SENSITIVITY_ORDER: readonly Sensitivity[] = ['public', 'internal', 'secret'] as const;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Return `true` iff `record` is allowed to flow to a sink declaring `accepts`.
|
|
25
|
+
*
|
|
26
|
+
* Comparison is **subset** semantics: the record's tier must be one of the
|
|
27
|
+
* tiers in `accepts` (it's not enough for the record's tier to be lower).
|
|
28
|
+
* That mirrors the way provider `acceptsSensitivity` is declared in the
|
|
29
|
+
* Graphorin trust matrix.
|
|
30
|
+
*
|
|
31
|
+
* @stable
|
|
32
|
+
*/
|
|
33
|
+
export function acceptsSensitivity(accepts: readonly Sensitivity[], record: Sensitivity): boolean {
|
|
34
|
+
return accepts.includes(record);
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifies a logical conversational scope: who the user is, which agent
|
|
3
|
+
* is in charge, and (when known) which session they're inside.
|
|
4
|
+
*
|
|
5
|
+
* Used as a parameter to almost every Memory / Sessions API. The agent and
|
|
6
|
+
* session fields are optional because some scopes are user-wide (e.g.
|
|
7
|
+
* shared facts attached to a user, not a particular agent).
|
|
8
|
+
*
|
|
9
|
+
* @stable
|
|
10
|
+
*/
|
|
11
|
+
export interface SessionScope {
|
|
12
|
+
/** Stable identifier of the user (single-user-per-process by default). */
|
|
13
|
+
readonly userId: string;
|
|
14
|
+
/** Identifier of the agent owning the scope, when applicable. */
|
|
15
|
+
readonly agentId?: string;
|
|
16
|
+
/** Identifier of the session, when applicable. */
|
|
17
|
+
readonly sessionId?: string;
|
|
18
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { RunState } from './run.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Predicate consulted by the agent runtime after every step to decide
|
|
5
|
+
* whether the loop should stop.
|
|
6
|
+
*
|
|
7
|
+
* Stop conditions are pure - they look at the current `RunState` and
|
|
8
|
+
* return a boolean. The runtime never re-orders or short-circuits the
|
|
9
|
+
* order in which operands of `and` / `or` are evaluated, so users can
|
|
10
|
+
* rely on the obvious left-to-right semantics.
|
|
11
|
+
*
|
|
12
|
+
* @stable
|
|
13
|
+
*/
|
|
14
|
+
export interface StopCondition {
|
|
15
|
+
/** Human-friendly label included in observability spans. */
|
|
16
|
+
readonly description: string;
|
|
17
|
+
/** Returns `true` when the run should stop on this state. */
|
|
18
|
+
readonly check: (state: RunState) => boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Stop after `n` total steps (`stepNumber >= n`). The default condition
|
|
23
|
+
* for the agent runtime is `isStepCount(50)`.
|
|
24
|
+
*
|
|
25
|
+
* @stable
|
|
26
|
+
*/
|
|
27
|
+
export function isStepCount(n: number): StopCondition {
|
|
28
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
29
|
+
throw new RangeError(`isStepCount: n must be a positive integer (got ${String(n)})`);
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
description: `step >= ${n}`,
|
|
33
|
+
check: (state): boolean => state.steps.length >= n,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Stop as soon as the most recent assistant message contains a tool call
|
|
39
|
+
* with the given name.
|
|
40
|
+
*
|
|
41
|
+
* @stable
|
|
42
|
+
*/
|
|
43
|
+
export function hasToolCall(toolName: string): StopCondition {
|
|
44
|
+
return {
|
|
45
|
+
description: `tool-call:${toolName}`,
|
|
46
|
+
check: (state): boolean => {
|
|
47
|
+
for (let i = state.messages.length - 1; i >= 0; i--) {
|
|
48
|
+
const msg = state.messages[i];
|
|
49
|
+
if (msg && msg.role === 'assistant') {
|
|
50
|
+
const calls = msg.toolCalls;
|
|
51
|
+
if (calls?.some((c) => c.toolName === toolName)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
// First assistant message without the call → not yet matched.
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Stop when the run reaches a terminal status.
|
|
65
|
+
*
|
|
66
|
+
* @stable
|
|
67
|
+
*/
|
|
68
|
+
export const isTerminal: StopCondition = {
|
|
69
|
+
description: 'status:terminal',
|
|
70
|
+
check: (state): boolean =>
|
|
71
|
+
state.status === 'completed' || state.status === 'failed' || state.status === 'aborted',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Stop when **any** of the supplied conditions is satisfied.
|
|
76
|
+
*
|
|
77
|
+
* @stable
|
|
78
|
+
*/
|
|
79
|
+
export function or(...conditions: readonly StopCondition[]): StopCondition {
|
|
80
|
+
return {
|
|
81
|
+
description: `or(${conditions.map((c) => c.description).join(', ')})`,
|
|
82
|
+
check: (state): boolean => conditions.some((c) => c.check(state)),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Stop only when **all** of the supplied conditions are satisfied.
|
|
88
|
+
*
|
|
89
|
+
* @stable
|
|
90
|
+
*/
|
|
91
|
+
export function and(...conditions: readonly StopCondition[]): StopCondition {
|
|
92
|
+
return {
|
|
93
|
+
description: `and(${conditions.map((c) => c.description).join(', ')})`,
|
|
94
|
+
check: (state): boolean => conditions.every((c) => c.check(state)),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Negate the supplied condition.
|
|
100
|
+
*
|
|
101
|
+
* @stable
|
|
102
|
+
*/
|
|
103
|
+
export function not(condition: StopCondition): StopCondition {
|
|
104
|
+
return {
|
|
105
|
+
description: `not(${condition.description})`,
|
|
106
|
+
check: (state): boolean => !condition.check(state),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The {@link ToolCall} leaf type lives in its own module so that
|
|
3
|
+
* `message.ts` (which references `ToolCall`) and `tool.ts` (which
|
|
4
|
+
* references `MessageContent`) can both depend on it without forming a
|
|
5
|
+
* circular import. `tool.ts` re-exports `ToolCall` for backwards
|
|
6
|
+
* compatibility, so external import paths are unchanged.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A single tool invocation the model requested, normalised by the
|
|
13
|
+
* provider layer and handed to the tool executor for parallel dispatch.
|
|
14
|
+
*
|
|
15
|
+
* @stable
|
|
16
|
+
*/
|
|
17
|
+
export interface ToolCall {
|
|
18
|
+
/** Stable identifier the model uses to correlate input and output. */
|
|
19
|
+
readonly toolCallId: string;
|
|
20
|
+
/** Tool name as registered in the `ToolRegistry`. */
|
|
21
|
+
readonly toolName: string;
|
|
22
|
+
/** Validated input matching the tool's `inputSchema`. */
|
|
23
|
+
readonly args: unknown;
|
|
24
|
+
}
|