deepccc 0.2.25 → 0.2.27

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.
@@ -105,7 +105,7 @@ export declare class DeepCccWebRuntime {
105
105
  deleteAttachment(sessionId: string, attachmentId: string): Promise<boolean>;
106
106
  readArtifact(sessionId: string, value: string): Promise<{
107
107
  path: string;
108
- mimeType: "image/png" | "image/jpeg" | "image/webp";
108
+ mimeType: "image/png" | "image/jpeg" | "image/webp" | "image/gif";
109
109
  size: number;
110
110
  bytes: NonSharedBuffer;
111
111
  } | null>;
@@ -37,6 +37,7 @@ export class DeepCccWebRuntime {
37
37
  baseURL: input.config.baseURL,
38
38
  model: input.meta.model || input.config.model,
39
39
  subModel: input.meta.subModel || input.config.subModel,
40
+ visionModel: input.config.visionModel,
40
41
  effort: input.meta.effort || input.config.effort,
41
42
  maxOutputTokens: input.config.maxOutputTokens,
42
43
  streaming: input.config.streaming,
@@ -44,6 +45,8 @@ export class DeepCccWebRuntime {
44
45
  cwd: input.meta.cwd,
45
46
  persist: true,
46
47
  sessionId: input.meta.sessionId,
48
+ contextDir: this.store.rootDir,
49
+ attachmentStore: this.attachmentStore,
47
50
  contextWindow: input.config.contextWindow,
48
51
  permissionMode: "ask",
49
52
  permissionResolver: input.permissionResolver,
@@ -6,6 +6,7 @@ export interface PublicDeepCccConfig {
6
6
  baseURL: string;
7
7
  model: string;
8
8
  subModel: string;
9
+ visionModel?: string;
9
10
  effort: string;
10
11
  maxOutputTokens?: number;
11
12
  streaming: boolean;
@@ -23,6 +23,7 @@ function runtimeConfig() {
23
23
  baseURL: config.baseURL,
24
24
  model: config.model,
25
25
  subModel: config.subModel,
26
+ visionModel: config.visionModel,
26
27
  effort: config.effort,
27
28
  maxOutputTokens: config.maxOutputTokens,
28
29
  contextWindow: config.contextWindow,
@@ -35,6 +36,7 @@ export function toPublicConfig(config, defaultCwd = process.cwd()) {
35
36
  baseURL: config.baseURL,
36
37
  model: config.model,
37
38
  subModel: config.subModel,
39
+ visionModel: config.visionModel,
38
40
  effort: config.effort,
39
41
  ...(config.maxOutputTokens ? { maxOutputTokens: config.maxOutputTokens } : {}),
40
42
  streaming: config.streaming,
@@ -1,36 +1,40 @@
1
- # Caller-owned completions
2
-
3
- `CompletionSession` is an opt-in stateless OpenAI-compatible transport. Pass the
4
- entire `messages` array; no system guidance, workspace files, skills, tools,
5
- history or compaction is injected. This is useful when comparing integrations
6
- without rewriting their established prompt builders.
7
-
8
- ```js
9
- import {CompletionSession} from 'deepccc';
10
- const session = new CompletionSession({
11
- apiKey: process.env.DEEPCCC_API_KEY,
12
- baseURL: process.env.DEEPCCC_BASE_URL,
13
- model: process.env.DEEPCCC_MODEL,
14
- });
15
- const result = await session.complete({
16
- messages: [{role: 'system', content: 'Return JSON only.'},
17
- {role: 'user', content: 'Return an object with ok=true.'}],
18
- json: true, timeoutMs: 5000, maxOutputTokens: 256, temperature: 0.1,
19
- });
20
- if (result.status === 'success') console.log(result.content);
21
- ```
22
-
23
- JSON mode sends `response_format: {type: 'json_object'}`; callers still parse,
24
- validate and authorize results. No local syntax repair or automatic retry is
25
- performed here. Completion is accepted only after a normal stop, never a length
26
- limit or interrupted stream. Text mode preserves plain-text output. Parallel
27
- calls do not share mutable history. `timeoutMs` covers stream consumption;
28
- optional cancellation is the second argument. Context/response character limits
29
- default to one million each. Oversize input and invalid options throw before
30
- network activity; runtime failures return fixed diagnostics without raw errors.
31
-
32
- Optional `extensions` passes caller-supplied provider-specific JSON options.
33
- It cannot override messages, model, streaming, tool settings, response format,
34
- temperature or token limits. Keep credentials in config, not extensions. No
35
- messages, headers or generated content are logged or persisted by this class.
36
- Metrics include duration and request count. Remote cancellation is best effort.
1
+ # Caller-owned completions
2
+
3
+ `CompletionSession` is an opt-in stateless OpenAI-compatible transport. Pass the
4
+ entire `messages` array; no system guidance, workspace files, skills, tools,
5
+ history or compaction is injected. This is useful when comparing integrations
6
+ without rewriting their established prompt builders.
7
+
8
+ ```js
9
+ import {CompletionSession} from 'deepccc';
10
+ const session = new CompletionSession({
11
+ apiKey: process.env.DEEPCCC_API_KEY,
12
+ baseURL: process.env.DEEPCCC_BASE_URL,
13
+ model: process.env.DEEPCCC_MODEL,
14
+ });
15
+ const result = await session.complete({
16
+ messages: [{role: 'system', content: 'Return JSON only.'},
17
+ {role: 'user', content: 'Return an object with ok=true.'}],
18
+ json: true, timeoutMs: 5000, maxOutputTokens: 256, temperature: 0.1,
19
+ });
20
+ if (result.status === 'success') console.log(result.content);
21
+ ```
22
+
23
+ JSON mode sends `response_format: {type: 'json_object'}`; callers still parse,
24
+ validate and authorize results. No local syntax repair or automatic retry is
25
+ performed here. Completion is accepted only after a normal stop, never a length
26
+ limit or interrupted stream. Text mode preserves plain-text output. Parallel
27
+ calls do not share mutable history. `timeoutMs` covers stream consumption;
28
+ optional cancellation is the second argument. Context/response character limits
29
+ default to one million each. Oversize input and invalid options throw before
30
+ network activity; runtime failures return fixed diagnostics without raw errors.
31
+
32
+ JSON responses are collected as text without constructing partial objects for
33
+ each streamed fragment. This avoids unnecessary parsing and allocation for large
34
+ responses. Ordinary ChatSession, CLI and Web streaming behavior is unchanged.
35
+
36
+ Optional `extensions` passes caller-supplied provider-specific JSON options.
37
+ It cannot override messages, model, streaming, tool settings, response format,
38
+ temperature or token limits. Keep credentials in config, not extensions. No
39
+ messages, headers or generated content are logged or persisted by this class.
40
+ Metrics include duration and request count. Remote cancellation is best effort.
@@ -1,192 +1,196 @@
1
- # Bounded structured decisions
2
-
3
- `DecisionSession` is an opt-in, programmatic, text-only API for integrations that
4
- own their state and need a bounded decision loop. Existing `ChatSession`, CLI and
5
- Web behavior is unchanged. The model client is reusable, but each `decide()` call
6
- starts with exactly the supplied context. No conversation history, workspace
7
- instructions, global skills, file tools, model-based compaction or raw stream
8
- logging is loaded automatically.
9
-
10
- This is not a simulator, a game-specific policy or a guarantee of model latency.
11
- Use an adapter to provide observations, validate decisions against current state,
12
- and execute actions. Keep application knowledge and private data outside the
13
- package. Read any skill references at application setup, then pass the relevant
14
- instructions through `systemPrompt`.
15
-
16
- ## Minimal integration
17
-
18
- ```js
19
- import { DecisionSession } from 'deepccc';
20
- import { jsonSchema } from 'ai';
21
-
22
- const outputSchema = jsonSchema({
23
- type: 'object',
24
- properties: { action: { type: 'string', enum: ['left', 'right', 'wait'] } },
25
- required: ['action'],
26
- additionalProperties: false,
27
- }, {
28
- validate(value) {
29
- if (value && typeof value === 'object' && !Array.isArray(value)
30
- && Object.keys(value).length === 1
31
- && ['left', 'right', 'wait'].includes(value.action)) {
32
- return { success: true, value };
33
- }
34
- return { success: false, error: new Error('Invalid decision') };
35
- },
36
- });
37
-
38
- const session = new DecisionSession({
39
- model: process.env.DEEPCCC_MODEL,
40
- effort: '', // Omit effort when the target service does not support it.
41
- maxOutputTokens: 2048,
42
- }, {
43
- systemPrompt: 'Choose a safe action toward the supplied goal. Unknown data stays unknown.',
44
- outputSchema,
45
- maxSteps: 1,
46
- maxContextChars: 32000,
47
- });
48
-
49
- const result = await session.decide({
50
- requestId: 'example-request-1',
51
- stateVersion: 3,
52
- timeoutMs: 5000,
53
- context: {
54
- observation: { position: 1, blocked: [0] },
55
- goal: { position: 4 },
56
- plan: null,
57
- memory: [],
58
- },
59
- });
60
-
61
- if (result.status === 'success') {
62
- // Re-check result.stateVersion and legality in the adapter before execution.
63
- console.log(result.value);
64
- } else {
65
- console.log(result.status); // Never execute partial text as an action.
66
- }
67
- ```
68
-
69
- The default Provider/model/credentials are resolved in the same way as
70
- `ChatSession`. This API always consumes a structured streaming response;
71
- `streaming` and `subModel` from ChatSession configuration are not decision options.
72
- There is no summarization call or hidden model switch.
73
-
74
- `outputSchema` and allowed tool input schemas **must contain runtime validators**.
75
- Use a Standard Schema supported by the AI SDK, or `jsonSchema(schema, {validate})`.
76
- A JSON schema alone describes the model contract but is not a runtime safety
77
- check, so the constructor rejects schemas without validators. Validators may be
78
- asynchronous and share the request deadline. The provider must support tool calls.
79
- The default `outputMode: 'tools'` uses the reserved `submit_result` tool; free-form
80
- JSON text is not accepted as an alternative in that mode.
81
-
82
- Since 0.2.24, `outputMode: 'json'` is available for OpenAI-compatible endpoints.
83
- It sends `response_format: {type: 'json_object'}` with no tools or tool choice;
84
- the schema is included in the system prompt and runtime validation is still
85
- mandatory. Nonempty `allowedTools` and other provider protocols are rejected in
86
- this mode. JSON syntax constraints do not guarantee semantic correctness.
87
- One local fallback removes trailing commas outside quoted strings, preserving
88
- values and escapes. Missing values, truncated streams, prose and invalid schemas
89
- are rejected; no extra model request is made for syntax recovery. Optional
90
- `metrics.jsonRepairs` counts repaired responses. Deadlines, cancellation, size
91
- limits, freshness checks and caller-owned context remain unchanged.
92
-
93
- ## Explicit capabilities
94
-
95
- Supply a `tools` record with `description`, `inputSchema` and `execute`, and list
96
- the capabilities to expose in `allowedTools`. Supplying a tool does **not** enable
97
- it. Both default to empty. Tool names must begin with a letter and use only
98
- letters, digits or underscores (at most 64 characters); `submit_result` is reserved.
99
-
100
- ```js
101
- const options = {
102
- systemPrompt: 'Inspect relevant information only when needed, then submit a result.',
103
- outputSchema,
104
- maxSteps: 2, // one inspection step plus one final submission
105
- maxToolCalls: 2,
106
- allowedTools: ['inspect_state'],
107
- tools: {
108
- inspect_state: {
109
- description: 'Return a compact observation for the supplied request.',
110
- inputSchema: jsonSchema({ type: 'object', additionalProperties: false }, {
111
- validate(value) {
112
- return value && typeof value === 'object' && !Array.isArray(value)
113
- && Object.keys(value).length === 0
114
- ? { success: true, value }
115
- : { success: false, error: new Error('Expected empty input') };
116
- },
117
- }),
118
- async execute(input, { signal, requestId, stateVersion }) {
119
- // Your adapter must honor signal and return a small JSON-serializable value.
120
- return adapter.observe({ signal, requestId, stateVersion });
121
- },
122
- },
123
- },
124
- };
125
- ```
126
-
127
- The entire model tool batch is checked before any tool runs. Unknown names,
128
- invalid inputs, duplicate call IDs and mixing a final submission with other
129
- calls fail closed. Tools execute sequentially and receive the same cancellation
130
- signal and request identity. They are not given to the SDK for automatic execution.
131
- The last permitted model step must submit a result; it cannot start more tools.
132
-
133
- Prefer observation-only capabilities. A tool allowlist is not an OS sandbox.
134
- Register trusted code only. Cancellation cannot undo effects of a tool that already
135
- started. Idempotency, freshness and actual execution acknowledgements are adapter
136
- responsibilities. Returned identity allows the adapter to reject stale decisions;
137
- the core does not know whether external state has changed.
138
-
139
- ## Budgets and results
140
-
141
- | Option | Default | Meaning |
142
- | --- | --- | --- |
143
- | request `timeoutMs` | Required | One wall-clock budget across generation, tools and validation |
144
- | `maxSteps` | 1 | Model requests, including final submission |
145
- | `maxToolCalls` | 4 | Total non-submission calls in a decision |
146
- | `maxContextChars` | 32000 | System prompt plus serialized messages |
147
- | `maxToolResultChars` | 8000 | Each serialized observation |
148
- | `maxResponseChars` | 32000 | Bounded streamed text/tool data; may count tool arguments twice |
149
- | model `maxOutputTokens` | Configured value, otherwise 2048 | Per-request provider output budget, including reasoning where applicable |
150
-
151
- Character budgets are protective size limits, not token estimates. Oversized
152
- state/results fail with `context_limit`; they are never silently truncated or
153
- summarized. Keep raw logs outside decision context. Assemble the latest state,
154
- active plan and selected evidence before calling the API. Background summaries
155
- should produce versioned memory snapshots rather than mutate an in-flight request.
156
-
157
- `success` is the only status carrying a validated `value`. Other statuses are
158
- `timed_out`, `cancelled`, `invalid_output`, `step_limit`, `tool_error`,
159
- `context_limit` and `error`. Metrics report total/model/tool milliseconds, model
160
- and tool call counts, and the last assembled context size. Provider/tool error
161
- payloads and partial generated content are not copied into result messages.
162
- Invalid configuration/request budgets throw before contacting the provider.
163
-
164
- Since 0.2.23, failures also include a safe `diagnostic` object with a fixed `code`
165
- and `stage`. Categories distinguish provider errors, invalid tool input, result
166
- schema failures, truncated output, interrupted output, deadlines and cancellation.
167
- Provider HTTP status is included when available, but error messages, raw tool
168
- arguments, prompts, headers and response bodies are never copied into diagnostics.
169
- The SDK's default raw error logging is disabled for this API. Integrations can
170
- record their own schema issue paths inside their validators; keep values and raw
171
- payloads out of those records. The original `status` remains the control-flow
172
- contract. Diagnostics do not relax validation or trigger automatic retries.
173
-
174
- Pass an optional `AbortSignal` as the second argument to `decide()`. Provider SDK
175
- automatic retries are disabled; any future application retry needs a fresh,
176
- explicit budget. Non-cooperative asynchronous tools/streams are raced against
177
- cancellation, and late results do not continue the decision loop. Cleanup is
178
- best-effort; it cannot guarantee remote inference billing stops or an external
179
- side effect is rolled back. Synchronous code that blocks the Node event loop
180
- cannot be preempted by a JavaScript timer: use a worker/process for heavy work.
181
-
182
- A deadline bounds waiting, not the probability of receiving a good result.
183
- Measure valid-result rate, median/p95 decision latency and application outcomes
184
- on the actual endpoint. Do not count a timeout as a fast successful decision.
185
-
186
- ## Release checks
187
-
188
- Tests use synthetic states and a loopback model endpoint, never production data.
189
- For publication, inspect staged changes, outgoing commits and the actual packed
190
- archive (including compiled output), and review public PR/release text. Never put
191
- credentials, private endpoints, proprietary assets or application traces into
192
- examples, tests, documentation or package files.
1
+ # Bounded structured decisions
2
+
3
+ `DecisionSession` is an opt-in, programmatic, text-only API for integrations that
4
+ own their state and need a bounded decision loop. Existing `ChatSession`, CLI and
5
+ Web behavior is unchanged. The model client is reusable, but each `decide()` call
6
+ starts with exactly the supplied context. No conversation history, workspace
7
+ instructions, global skills, file tools, model-based compaction or raw stream
8
+ logging is loaded automatically.
9
+
10
+ This is not a simulator, a game-specific policy or a guarantee of model latency.
11
+ Use an adapter to provide observations, validate decisions against current state,
12
+ and execute actions. Keep application knowledge and private data outside the
13
+ package. Read any skill references at application setup, then pass the relevant
14
+ instructions through `systemPrompt`.
15
+
16
+ ## Minimal integration
17
+
18
+ ```js
19
+ import { DecisionSession } from 'deepccc';
20
+ import { jsonSchema } from 'ai';
21
+
22
+ const outputSchema = jsonSchema({
23
+ type: 'object',
24
+ properties: { action: { type: 'string', enum: ['left', 'right', 'wait'] } },
25
+ required: ['action'],
26
+ additionalProperties: false,
27
+ }, {
28
+ validate(value) {
29
+ if (value && typeof value === 'object' && !Array.isArray(value)
30
+ && Object.keys(value).length === 1
31
+ && ['left', 'right', 'wait'].includes(value.action)) {
32
+ return { success: true, value };
33
+ }
34
+ return { success: false, error: new Error('Invalid decision') };
35
+ },
36
+ });
37
+
38
+ const session = new DecisionSession({
39
+ model: process.env.DEEPCCC_MODEL,
40
+ effort: '', // Omit effort when the target service does not support it.
41
+ maxOutputTokens: 2048,
42
+ }, {
43
+ systemPrompt: 'Choose a safe action toward the supplied goal. Unknown data stays unknown.',
44
+ outputSchema,
45
+ maxSteps: 1,
46
+ maxContextChars: 32000,
47
+ });
48
+
49
+ const result = await session.decide({
50
+ requestId: 'example-request-1',
51
+ stateVersion: 3,
52
+ timeoutMs: 5000,
53
+ context: {
54
+ observation: { position: 1, blocked: [0] },
55
+ goal: { position: 4 },
56
+ plan: null,
57
+ memory: [],
58
+ },
59
+ });
60
+
61
+ if (result.status === 'success') {
62
+ // Re-check result.stateVersion and legality in the adapter before execution.
63
+ console.log(result.value);
64
+ } else {
65
+ console.log(result.status); // Never execute partial text as an action.
66
+ }
67
+ ```
68
+
69
+ The default Provider/model/credentials are resolved in the same way as
70
+ `ChatSession`. This API always consumes a structured streaming response;
71
+ `streaming` and `subModel` from ChatSession configuration are not decision options.
72
+ There is no summarization call or hidden model switch.
73
+
74
+ `outputSchema` and allowed tool input schemas **must contain runtime validators**.
75
+ Use a Standard Schema supported by the AI SDK, or `jsonSchema(schema, {validate})`.
76
+ A JSON schema alone describes the model contract but is not a runtime safety
77
+ check, so the constructor rejects schemas without validators. Validators may be
78
+ asynchronous and share the request deadline. The provider must support tool calls.
79
+ The default `outputMode: 'tools'` uses the reserved `submit_result` tool; free-form
80
+ JSON text is not accepted as an alternative in that mode.
81
+
82
+ Since 0.2.24, `outputMode: 'json'` is available for OpenAI-compatible endpoints.
83
+ It sends `response_format: {type: 'json_object'}` with no tools or tool choice;
84
+ the schema is included in the system prompt and runtime validation is still
85
+ mandatory. Nonempty `allowedTools` and other provider protocols are rejected in
86
+ this mode. JSON syntax constraints do not guarantee semantic correctness.
87
+ One local fallback removes trailing commas outside quoted strings, preserving
88
+ values and escapes. Missing values, truncated streams, prose and invalid schemas
89
+ are rejected; no extra model request is made for syntax recovery. Optional
90
+ `metrics.jsonRepairs` counts repaired responses. Deadlines, cancellation, size
91
+ limits, freshness checks and caller-owned context remain unchanged.
92
+
93
+ JSON mode collects streamed text and parses the completed response once, rather
94
+ than constructing intermediate partial objects. Final syntax recovery and schema
95
+ validation remain mandatory. Tools mode and ordinary ChatSession are unchanged.
96
+
97
+ ## Explicit capabilities
98
+
99
+ Supply a `tools` record with `description`, `inputSchema` and `execute`, and list
100
+ the capabilities to expose in `allowedTools`. Supplying a tool does **not** enable
101
+ it. Both default to empty. Tool names must begin with a letter and use only
102
+ letters, digits or underscores (at most 64 characters); `submit_result` is reserved.
103
+
104
+ ```js
105
+ const options = {
106
+ systemPrompt: 'Inspect relevant information only when needed, then submit a result.',
107
+ outputSchema,
108
+ maxSteps: 2, // one inspection step plus one final submission
109
+ maxToolCalls: 2,
110
+ allowedTools: ['inspect_state'],
111
+ tools: {
112
+ inspect_state: {
113
+ description: 'Return a compact observation for the supplied request.',
114
+ inputSchema: jsonSchema({ type: 'object', additionalProperties: false }, {
115
+ validate(value) {
116
+ return value && typeof value === 'object' && !Array.isArray(value)
117
+ && Object.keys(value).length === 0
118
+ ? { success: true, value }
119
+ : { success: false, error: new Error('Expected empty input') };
120
+ },
121
+ }),
122
+ async execute(input, { signal, requestId, stateVersion }) {
123
+ // Your adapter must honor signal and return a small JSON-serializable value.
124
+ return adapter.observe({ signal, requestId, stateVersion });
125
+ },
126
+ },
127
+ },
128
+ };
129
+ ```
130
+
131
+ The entire model tool batch is checked before any tool runs. Unknown names,
132
+ invalid inputs, duplicate call IDs and mixing a final submission with other
133
+ calls fail closed. Tools execute sequentially and receive the same cancellation
134
+ signal and request identity. They are not given to the SDK for automatic execution.
135
+ The last permitted model step must submit a result; it cannot start more tools.
136
+
137
+ Prefer observation-only capabilities. A tool allowlist is not an OS sandbox.
138
+ Register trusted code only. Cancellation cannot undo effects of a tool that already
139
+ started. Idempotency, freshness and actual execution acknowledgements are adapter
140
+ responsibilities. Returned identity allows the adapter to reject stale decisions;
141
+ the core does not know whether external state has changed.
142
+
143
+ ## Budgets and results
144
+
145
+ | Option | Default | Meaning |
146
+ | --- | --- | --- |
147
+ | request `timeoutMs` | Required | One wall-clock budget across generation, tools and validation |
148
+ | `maxSteps` | 1 | Model requests, including final submission |
149
+ | `maxToolCalls` | 4 | Total non-submission calls in a decision |
150
+ | `maxContextChars` | 32000 | System prompt plus serialized messages |
151
+ | `maxToolResultChars` | 8000 | Each serialized observation |
152
+ | `maxResponseChars` | 32000 | Bounded streamed text/tool data; may count tool arguments twice |
153
+ | model `maxOutputTokens` | Configured value, otherwise 2048 | Per-request provider output budget, including reasoning where applicable |
154
+
155
+ Character budgets are protective size limits, not token estimates. Oversized
156
+ state/results fail with `context_limit`; they are never silently truncated or
157
+ summarized. Keep raw logs outside decision context. Assemble the latest state,
158
+ active plan and selected evidence before calling the API. Background summaries
159
+ should produce versioned memory snapshots rather than mutate an in-flight request.
160
+
161
+ `success` is the only status carrying a validated `value`. Other statuses are
162
+ `timed_out`, `cancelled`, `invalid_output`, `step_limit`, `tool_error`,
163
+ `context_limit` and `error`. Metrics report total/model/tool milliseconds, model
164
+ and tool call counts, and the last assembled context size. Provider/tool error
165
+ payloads and partial generated content are not copied into result messages.
166
+ Invalid configuration/request budgets throw before contacting the provider.
167
+
168
+ Since 0.2.23, failures also include a safe `diagnostic` object with a fixed `code`
169
+ and `stage`. Categories distinguish provider errors, invalid tool input, result
170
+ schema failures, truncated output, interrupted output, deadlines and cancellation.
171
+ Provider HTTP status is included when available, but error messages, raw tool
172
+ arguments, prompts, headers and response bodies are never copied into diagnostics.
173
+ The SDK's default raw error logging is disabled for this API. Integrations can
174
+ record their own schema issue paths inside their validators; keep values and raw
175
+ payloads out of those records. The original `status` remains the control-flow
176
+ contract. Diagnostics do not relax validation or trigger automatic retries.
177
+
178
+ Pass an optional `AbortSignal` as the second argument to `decide()`. Provider SDK
179
+ automatic retries are disabled; any future application retry needs a fresh,
180
+ explicit budget. Non-cooperative asynchronous tools/streams are raced against
181
+ cancellation, and late results do not continue the decision loop. Cleanup is
182
+ best-effort; it cannot guarantee remote inference billing stops or an external
183
+ side effect is rolled back. Synchronous code that blocks the Node event loop
184
+ cannot be preempted by a JavaScript timer: use a worker/process for heavy work.
185
+
186
+ A deadline bounds waiting, not the probability of receiving a good result.
187
+ Measure valid-result rate, median/p95 decision latency and application outcomes
188
+ on the actual endpoint. Do not count a timeout as a fast successful decision.
189
+
190
+ ## Release checks
191
+
192
+ Tests use synthetic states and a loopback model endpoint, never production data.
193
+ For publication, inspect staged changes, outgoing commits and the actual packed
194
+ archive (including compiled output), and review public PR/release text. Never put
195
+ credentials, private endpoints, proprietary assets or application traces into
196
+ examples, tests, documentation or package files.