@robota-sdk/agent-core 3.0.0-beta.86 → 3.0.0-beta.88
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 +36 -15
- package/README.md +56 -22
- package/dist/browser/{bounded-output-Bgx2Cgkx.js → bounded-output-BdWv9SX3.js} +2 -2
- package/dist/browser/bounded-output-BdWv9SX3.js.map +1 -0
- package/dist/browser/{command-executor-C0XXfBnt.js → command-executor-DIXaSMrr.js} +2 -2
- package/dist/{node/command-executor-d6DdfUTD.js.map → browser/command-executor-DIXaSMrr.js.map} +1 -1
- package/dist/browser/index.d.ts +140 -48
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +10 -9
- package/dist/browser/index.js.map +1 -1
- package/dist/node/{bounded-output-Bgx2Cgkx.js → bounded-output-BdWv9SX3.js} +2 -2
- package/dist/node/bounded-output-BdWv9SX3.js.map +1 -0
- package/dist/node/{command-executor-d6DdfUTD.js → command-executor-D_UBZHxh.js} +2 -2
- package/dist/{browser/command-executor-C0XXfBnt.js.map → node/command-executor-D_UBZHxh.js.map} +1 -1
- package/dist/node/{command-executor-CqAFpB-X.cjs → command-executor-wBgBdWXr.cjs} +1 -1
- package/dist/node/{http-executor-BFtXs8FP.cjs → http-executor-CeAAZotH.cjs} +1 -1
- package/dist/node/{index-CPwJEiFl.d.cts → index-CagopdzH.d.cts} +141 -49
- package/dist/node/index-CagopdzH.d.cts.map +1 -0
- package/dist/node/{index-DQrmrFn9.d.ts → index-CjyNzgQN.d.ts} +141 -49
- package/dist/node/index-CjyNzgQN.d.ts.map +1 -0
- package/dist/node/index.cjs +10 -9
- package/dist/node/index.d.cts +2 -2
- package/dist/node/index.d.ts +2 -2
- package/dist/node/index.js +12 -11
- package/dist/node/index.js.map +1 -1
- package/dist/node/node.cjs +1 -1
- package/dist/node/node.d.cts +13 -2
- package/dist/node/node.d.cts.map +1 -1
- package/dist/node/node.d.ts +13 -2
- package/dist/node/node.d.ts.map +1 -1
- package/dist/node/node.js +1 -1
- package/dist/node/node.js.map +1 -1
- package/dist/node/testing/index.d.cts +1 -1
- package/dist/node/testing/index.d.ts +1 -1
- package/dist/node/{verdict-decoder-DIHay1ul.cjs → verdict-decoder-DQqwwDE7.cjs} +1 -1
- package/package.json +10 -10
- package/dist/browser/bounded-output-Bgx2Cgkx.js.map +0 -1
- package/dist/node/bounded-output-Bgx2Cgkx.js.map +0 -1
- package/dist/node/index-CPwJEiFl.d.cts.map +0 -1
- package/dist/node/index-DQrmrFn9.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @robota-sdk/agent-core
|
|
2
2
|
|
|
3
|
+
## 3.0.0-beta.88
|
|
4
|
+
|
|
5
|
+
## 3.0.0-beta.87
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- Release the migrated Robota product with its public SDK namespace, robota command, compatible SDK exports, internal product configuration, and verified CLI startup and delivery corrections.
|
|
10
|
+
- 66af868: Accept Claude-compatible WebFetch domain permission rules during session initialization while preserving hostname boundaries and deny/ask enforcement.
|
|
11
|
+
- 7d9cc66: Add trusted host scheduling for model tool rounds, with dependencies, bounded independent work,
|
|
12
|
+
and shared-resource reader/writer exclusion. Unknown resources serialize conservatively. Failed
|
|
13
|
+
predecessors or invalid plans produce paired skipped receipts, while settled recovery receipts
|
|
14
|
+
and already running sibling outcomes remain intact.
|
|
15
|
+
- 7d9cc66: Retain typed text and image observations alongside structured tool data through runtime settlement, history, and recovery. Deliver tool images as native content in OpenAI Responses and Anthropic requests, retain structured error receipts, include media in bounded result admission, and reject malformed MCP image observations with reconciliation state intact.
|
|
16
|
+
- 7d9cc66: Preserve a result for each remaining call when a sequential tool batch stops after failure. Persist skipped calls without dispatch and reject recovery records that claim an executed action was skipped.
|
|
17
|
+
- 7d9cc66: Retain typed resource and audio observations alongside structured tool results through history, durable receipts, recovery, size admission, and session persistence. Preserve opaque resource references without fetching them, project embedded text and explicit unsupported binary/audio diagnostics into provider requests and compaction transcripts, and validate persisted observation shapes on reload.
|
|
18
|
+
- 7d9cc66: Retain registered tool source attribution in observations and preserve ordered outcome receipts independently of conversation summaries.
|
|
19
|
+
- 7d9cc66: Observe scheduler queue intervals separately from pre-dispatch admission, permission and tool work.
|
|
20
|
+
Preserve valid duration totals when live trace detail is bounded, reporting invalid evidence explicitly
|
|
21
|
+
through content-free console and OTLP traces without altering dispatch, recovery or settlement.
|
|
22
|
+
- 7d9cc66: Retain registered attribution on refused and interrupted call receipts without replacing the source of actual or recovered settlements, and project undispatched registered calls into live and restored presentation history.
|
|
23
|
+
|
|
3
24
|
## 3.0.0-beta.86
|
|
4
25
|
|
|
5
26
|
## 3.0.0-beta.85
|
|
@@ -20,7 +41,7 @@
|
|
|
20
41
|
|
|
21
42
|
Pair parallel tool analytics by the logical execution ID, including calls of the same tool.
|
|
22
43
|
|
|
23
|
-
- 5093a30: A `
|
|
44
|
+
- 5093a30: A `ConversationAgent` run now calls `beforeConversation`, with `beforeExecution`, and `onStreamingChunk` for
|
|
24
45
|
each streamed piece of text, in order; the round waits for those chunk hooks before it goes on,
|
|
25
46
|
whether the provider call returned, failed or was interrupted. `EventEmitterPlugin` therefore emits
|
|
26
47
|
`CONVERSATION_START`.
|
|
@@ -69,12 +90,12 @@
|
|
|
69
90
|
sees it as a tool failure it could retry. A journaled run waits for a cancelled model call to
|
|
70
91
|
finish, because its late reply can still carry usage or tool calls that must be recorded.
|
|
71
92
|
|
|
72
|
-
With a journal that can also read its records back (`IRecoverableExecutionJournal`), a new `
|
|
93
|
+
With a journal that can also read its records back (`IRecoverableExecutionJournal`), a new `ConversationAgent`
|
|
73
94
|
for the same conversation continues where the old one stopped, without new user input and without
|
|
74
95
|
repeating calls whose results were saved:
|
|
75
96
|
|
|
76
|
-
- `
|
|
77
|
-
- `
|
|
97
|
+
- `ConversationAgent.resumeToolCalls()` runs the latest saved batch of tool calls without calling the model.
|
|
98
|
+
- `ConversationAgent.resume()` continues the whole execution, keeping its remaining round and repeated-input
|
|
78
99
|
limits. It refuses a different provider or model. Turn-level lifecycle hooks do not run again, and
|
|
79
100
|
structured output cannot be recovered. A deferred tool the execution had loaded that is no longer
|
|
80
101
|
registered raises `ExecutionRecoveryError`.
|
|
@@ -193,7 +214,7 @@
|
|
|
193
214
|
The tool reaches only files inside the workspace whose path does not look like it holds secrets
|
|
194
215
|
(`.env*`, `~/.ssh`, keys and credentials), and it does not exist in a turn a peer's message started.
|
|
195
216
|
- The receiving operator approves every file. A received file is kept as an inert copy (mode 0600)
|
|
196
|
-
under
|
|
217
|
+
under `$PRODUCT_USER_STATE_DIR/peer-files/<sender>/`. It is never run and never placed in the model's context.
|
|
197
218
|
The conversation is told only its name, size and sha256. A name that leaves that directory is
|
|
198
219
|
refused, a symbolic link is never written through, and nothing is overwritten.
|
|
199
220
|
- Transfers travel on a channel of their own (a separate connection on this host, a separate data
|
|
@@ -270,9 +291,9 @@
|
|
|
270
291
|
session on an earlier version cannot confirm, so its messages are refused; both sessions need this
|
|
271
292
|
version to message each other.
|
|
272
293
|
|
|
273
|
-
- ec5e477: Add a credential store port (`ICredentialStore` in `agent-core`) and keep the CLI's secrets behind it: the OS keychain through the optional `@napi-rs/keyring` binding (macOS Keychain, Windows Credential Manager, Linux Secret Service), else an owner-only file under
|
|
294
|
+
- ec5e477: Add a credential store port (`ICredentialStore` in `agent-core`) and keep the CLI's secrets behind it: the OS keychain through the optional `@napi-rs/keyring` binding (macOS Keychain, Windows Credential Manager, Linux Secret Service), else an owner-only file under `$PRODUCT_USER_STATE_DIR/credentials`. The backend is chosen at first use, recorded, and named by `/remote-control status`; a recorded keychain that stops working fails closed instead of degrading to the file.
|
|
274
295
|
|
|
275
|
-
The remote-control host identity key moves into the store. The old
|
|
296
|
+
The remote-control host identity key moves into the store. The old `$PRODUCT_USER_STATE_DIR/remote-host-identity.json` may have been copied by backups or dotfile sync, so it is not carried over: on the first run after upgrading a new host key is generated, the old file is removed, and the operator is told once that trusted devices must pair again.
|
|
276
297
|
|
|
277
298
|
## 3.0.0-beta.80
|
|
278
299
|
|
|
@@ -311,8 +332,8 @@
|
|
|
311
332
|
construction alongside `allow` and `deny`.
|
|
312
333
|
- **Never auto-approved, bypass included:** removing a critical path with `rm`/`rmdir` (the root, a
|
|
313
334
|
top-level directory, home, the working directory or a parent), and a modify-class write into
|
|
314
|
-
`.git`, `.
|
|
315
|
-
Files inside an isolated worktree (
|
|
335
|
+
`.git`, `.Robota`, `.claude`, `.agents`, `.mcp.json`, `.gitconfig`, `.npmrc` or a shell rc file.
|
|
336
|
+
Files inside an isolated worktree (`$PRODUCT_PROJECT_STATE_DIR/worktrees/<name>/…`) are ordinary files. With no
|
|
316
337
|
approver attached, an ask is a denial.
|
|
317
338
|
- **A ceiling is checked before bypass and before any ask.** A subagent's `inherit-allowlist` ceiling
|
|
318
339
|
is now the parent's _effective_ rules, read live at spawn: settings, preset lists and command
|
|
@@ -474,7 +495,7 @@
|
|
|
474
495
|
of following it; registration, token and refresh requests use it.
|
|
475
496
|
- **Storage:** `IMCPOAuthCredentialStore` (`get`/`set`/`delete`), keyed by the server's security
|
|
476
497
|
identity and canonical URL together. `createFileOAuthCredentialStore` keeps 0600 files in a 0700
|
|
477
|
-
|
|
498
|
+
`$PRODUCT_USER_STATE_DIR/mcp-credentials/`. The issuer, token endpoint and client (with its secret, if any, and
|
|
478
499
|
the client authentication method dynamic registration returned) are stored with the tokens. A
|
|
479
500
|
sign-in stores its credential under the refresh lock, so a refresh in flight cannot overwrite it.
|
|
480
501
|
- **Sessions** (`createOAuthAuthenticator`, wired for every `oauth` definition):
|
|
@@ -889,7 +910,7 @@
|
|
|
889
910
|
|
|
890
911
|
- 6f308d1: fix(streaming): expose token usage on the streaming execution path (BEHAVIOR-005)
|
|
891
912
|
|
|
892
|
-
Token usage was silently dropped on streaming turns, so `readTokenUsageFromMessage` and
|
|
913
|
+
Token usage was silently dropped on streaming turns, so `readTokenUsageFromMessage` and Robota's usage analytics returned empty/0 for every `run()`/`runStream()` (which always stream). OpenAI-compatible streaming requests now send `stream_options: { include_usage: true }`, the stream assembler and the `runStream` commit path attach the same top-level `usage` shape the non-streaming path already emits, and both `run()` and `runStream()` now expose usage. New opt-out `IOpenAIProviderOptions.includeStreamUsage` (default `true`) for OpenAI-compatible servers that reject `stream_options`.
|
|
893
914
|
|
|
894
915
|
## 3.0.0-beta.77
|
|
895
916
|
|
|
@@ -908,7 +929,7 @@
|
|
|
908
929
|
### Patch Changes
|
|
909
930
|
|
|
910
931
|
- DQ-AUDIT-002 — consolidate duplicated domain data onto single owners: one model-pricing SSOT in agent-core (`MODEL_PRICES`/`lookupModelPrice`/`calculateModelCost`/`estimateBlendedCostPer1000`) consumed by agent-command and agent-plugin (drops two embedded/stale price tables); the `len/4` token estimator replaced by core `CONTEXT_ESTIMATE_CHARS_PER_TOKEN`; TUI `TContextState` derived from core `IContextWindowState`; dead pass-through re-exports removed from agent-session.
|
|
911
|
-
- DQ-AUDIT-006 — error/observability hygiene: replace raw `throw new Error()` on core-service and provider hot paths with typed `
|
|
932
|
+
- DQ-AUDIT-006 — error/observability hygiene: replace raw `throw new Error()` on core-service and provider hot paths with typed `AgentRuntimeError` subclasses (`ConfigurationError`/`ValidationError`) so error-handling can branch on category/recoverable; surface fire-and-forget hook failures via `logger.warn` instead of silent `.catch(() => {})`; wire the error-handling plugin's `totalRetries`/`successfulRecoveries` stats to real counters.
|
|
912
933
|
- DQ-AUDIT-007 — remove the silent `model || 'gpt-4o-mini'` default in the OpenAI streaming handler (a missing model now throws `ConfigurationError` instead of substituting a vendor default); document `IAIProvider`'s universal (`chat`) vs raw (`generateResponse`) dual surface as intentional in the agent-core SPEC.
|
|
913
934
|
- 576af62: Fix `ConfigurationError: Agent must be fully initialized before changing model configuration` when running `/preset` (or any live model re-apply) on a fresh interactive session before the first message. The Robota agent initialized lazily on the first `run()`, but `setModel` requires full initialization. `Session.applyModelOptions` now awaits the new idempotent `Robota.ensureReady()` before `setModel`, and the preset live-switch path (`applyPresetToSession` → `executePresetCommand`) is async end-to-end. Adds a real cold-session regression test (no mocked Robota).
|
|
914
935
|
|
|
@@ -920,7 +941,7 @@
|
|
|
920
941
|
|
|
921
942
|
- **Preset system (PRESET-001~017):** new `@robota-sdk/agent-preset` package layering framework
|
|
922
943
|
assembly options into named, selectable profiles (`default`, `autonomous-builder`, `careful-reviewer`,
|
|
923
|
-
`neutral-executor`) plus user-authored external presets loaded from
|
|
944
|
+
`neutral-executor`) plus user-authored external presets loaded from `$PRODUCT_USER_STATE_DIR/presets/*.json`.
|
|
924
945
|
- **Live preset switching:** `/preset` command (list + active marker + switch) and a TUI active-preset
|
|
925
946
|
display. Switching live re-applies permission posture, model/effort, persona, command-module
|
|
926
947
|
selection, parallel-subagents gating, and a self-verification system-prompt section via the single
|
|
@@ -965,7 +986,7 @@
|
|
|
965
986
|
- Add CLI second-screen browser monitor (PLG-002)
|
|
966
987
|
- New `@robota-sdk/agent-web` package: WebSocket client, `useWsSession` hook, `SessionMonitor` component with Markdown rendering
|
|
967
988
|
- `--web` flag on `agent-cli`: starts WebSocket sidecar server and auto-opens browser monitor
|
|
968
|
-
- `--no-open` flag and `
|
|
989
|
+
- `--no-open` flag and `PRODUCT_NO_OPEN` env var to suppress browser launch
|
|
969
990
|
- `user_message` event added to `IInteractiveSessionEvents` so user prompts stream to browser in real-time
|
|
970
991
|
- `TServerMessage` protocol extended with `user_message` type
|
|
971
992
|
|
|
@@ -989,7 +1010,7 @@
|
|
|
989
1010
|
|
|
990
1011
|
### Patch Changes
|
|
991
1012
|
|
|
992
|
-
- Refresh package docs and
|
|
1013
|
+
- Refresh package docs and Robota.io content for the beta 57 feature set.
|
|
993
1014
|
|
|
994
1015
|
## 3.0.0-beta.57
|
|
995
1016
|
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# @robota-sdk/agent-core
|
|
2
2
|
|
|
3
|
-
The foundation of the Robota SDK. It provides the `
|
|
3
|
+
The foundation of the Robota SDK. It provides the `ConversationAgent` class (a conversation loop with
|
|
4
4
|
tool calling), the provider, tool and plugin contracts with their abstract base classes, the
|
|
5
5
|
permission evaluator, the hook runner, event services, typed errors, model metadata, and structured
|
|
6
|
-
output. It has no `@robota-sdk/*` dependencies; every other
|
|
6
|
+
output. It has no `@robota-sdk/*` dependencies; every other agent runtime package builds on it.
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
@@ -17,12 +17,12 @@ Requires Node.js 22.12 or later. A provider package supplies the model connectio
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
-
import {
|
|
20
|
+
import { ConversationAgent } from '@robota-sdk/agent-core';
|
|
21
21
|
import { AnthropicProvider } from '@robota-sdk/agent-provider-anthropic';
|
|
22
22
|
|
|
23
23
|
const provider = new AnthropicProvider({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
24
24
|
|
|
25
|
-
const agent = new
|
|
25
|
+
const agent = new ConversationAgent({
|
|
26
26
|
name: 'MyAgent',
|
|
27
27
|
aiProviders: [provider],
|
|
28
28
|
defaultModel: {
|
|
@@ -40,7 +40,7 @@ console.log(response);
|
|
|
40
40
|
|
|
41
41
|
## What it provides
|
|
42
42
|
|
|
43
|
-
- **`
|
|
43
|
+
- **`ConversationAgent`** — an agent with conversation history, automatic tool execution, plugins, streaming
|
|
44
44
|
(`runStream`) and structured output. By default history accumulates across runs; set
|
|
45
45
|
`retainHistory: false` to start every run from the system prompt.
|
|
46
46
|
- **Messages** — every message (`TUniversalMessage`) has a unique `id` and a `state`
|
|
@@ -52,27 +52,27 @@ console.log(response);
|
|
|
52
52
|
- **Tools** — `FunctionTool` and `ToolRegistry` live here, next to the `AbstractTool` base class and
|
|
53
53
|
the tool schema contract. Ready-made tools are in `@robota-sdk/agent-tools`.
|
|
54
54
|
- **Permissions** — `evaluatePermission` decides a tool call from the permission mode and
|
|
55
|
-
allow/deny/ask rules; it is the one evaluator every
|
|
55
|
+
allow/deny/ask rules; it is the one evaluator every agent runtime caller uses.
|
|
56
56
|
- **Hooks** — `runHooks` runs the lifecycle hooks configured for an event. See the
|
|
57
57
|
[hook event catalog](./docs/HOOK-CATALOG.md).
|
|
58
58
|
- **Plugins** — `AbstractPlugin` with lifecycle hooks (`beforeRun`, `afterRun`, `onError`, …).
|
|
59
59
|
Ready-made plugins are in `@robota-sdk/agent-plugin`.
|
|
60
60
|
- **Events** — event services with owner-path tracking and `EventEmitterPlugin`.
|
|
61
|
-
- **Errors** — typed errors extending `
|
|
61
|
+
- **Errors** — typed errors extending `AgentRuntimeError`, such as `ProviderError`, `RateLimitError`,
|
|
62
62
|
`ToolExecutionError` and `StructuredOutputError`.
|
|
63
63
|
- **Model metadata** — providers register their own models with `registerModelMetadata()`; core
|
|
64
64
|
owns the registry and the lookups (`getModelContextWindow()`, `getModelMaxOutput()`, …), not any
|
|
65
65
|
vendor's catalogue.
|
|
66
66
|
- **Cancellation** — an `AbortSignal` passed in run options reaches the provider call.
|
|
67
67
|
|
|
68
|
-
##
|
|
68
|
+
## agent runtime API
|
|
69
69
|
|
|
70
70
|
```typescript
|
|
71
|
-
import {
|
|
71
|
+
import { ConversationAgent } from '@robota-sdk/agent-core';
|
|
72
72
|
import type { IAgentConfig } from '@robota-sdk/agent-core';
|
|
73
73
|
|
|
74
74
|
declare const config: IAgentConfig;
|
|
75
|
-
const agent = new
|
|
75
|
+
const agent = new ConversationAgent(config);
|
|
76
76
|
|
|
77
77
|
// Send a message (tool calls are executed automatically)
|
|
78
78
|
const response = await agent.run('Hello');
|
|
@@ -97,11 +97,11 @@ exists, and the response is always validated by core with a bounded retry on vio
|
|
|
97
97
|
|
|
98
98
|
```typescript
|
|
99
99
|
import { z } from 'zod';
|
|
100
|
-
import {
|
|
100
|
+
import { ConversationAgent } from '@robota-sdk/agent-core';
|
|
101
101
|
import type { IAgentConfig } from '@robota-sdk/agent-core';
|
|
102
102
|
|
|
103
103
|
declare const config: IAgentConfig;
|
|
104
|
-
const agent = new
|
|
104
|
+
const agent = new ConversationAgent(config);
|
|
105
105
|
|
|
106
106
|
const reportSchema = z.object({
|
|
107
107
|
title: z.string(),
|
|
@@ -140,11 +140,11 @@ applies to the run's first model call only; later rounds revert to `'auto'` so t
|
|
|
140
140
|
tool results and finish.
|
|
141
141
|
|
|
142
142
|
```typescript
|
|
143
|
-
import {
|
|
143
|
+
import { ConversationAgent } from '@robota-sdk/agent-core';
|
|
144
144
|
import type { IAgentConfig } from '@robota-sdk/agent-core';
|
|
145
145
|
|
|
146
146
|
declare const config: IAgentConfig;
|
|
147
|
-
const agent = new
|
|
147
|
+
const agent = new ConversationAgent(config);
|
|
148
148
|
|
|
149
149
|
// Force the model to answer through a router tool (decision-agent pattern)
|
|
150
150
|
const decision = await agent.run('Route this request.', {
|
|
@@ -179,7 +179,7 @@ including `provider_request`, `provider_native_raw_payload`, `provider_stream_ra
|
|
|
179
179
|
and `history_mutation`. `@robota-sdk/agent-session` writes these events to its session log.
|
|
180
180
|
|
|
181
181
|
Capturing a vendor SDK's exact payloads stays in the provider package: a provider calls
|
|
182
|
-
`IChatOptions.onProviderNativeRawPayload`, and `
|
|
182
|
+
`IChatOptions.onProviderNativeRawPayload`, and `ConversationAgent` forwards it as a
|
|
183
183
|
`provider_native_raw_payload` event without importing vendor SDK types.
|
|
184
184
|
|
|
185
185
|
Each provider call gets a `usageObservationId`. Streaming fragments of one call share it, and a
|
|
@@ -189,11 +189,11 @@ separately billed call gets a new one, so stored analytics can deduplicate usage
|
|
|
189
189
|
one run, tool rounds included, is the sum over the messages that run appended:
|
|
190
190
|
|
|
191
191
|
```typescript
|
|
192
|
-
import {
|
|
192
|
+
import { ConversationAgent, sumMessagesUsage } from '@robota-sdk/agent-core';
|
|
193
193
|
import type { IAgentConfig } from '@robota-sdk/agent-core';
|
|
194
194
|
|
|
195
195
|
declare const config: IAgentConfig;
|
|
196
|
-
const agent = new
|
|
196
|
+
const agent = new ConversationAgent(config);
|
|
197
197
|
|
|
198
198
|
const before = agent.getHistory().length;
|
|
199
199
|
await agent.run('Summarize the open issues.');
|
|
@@ -220,6 +220,21 @@ The main fields:
|
|
|
220
220
|
| `maxExecutionRounds` | `number?` | Cap on model-call rounds per run (`0` means no cap) |
|
|
221
221
|
| `isToolVisible` | `(name) => boolean` | Hide a registered tool from the model, checked every round |
|
|
222
222
|
|
|
223
|
+
`toolExecutionPolicy(calls)` is a trusted host callback evaluated for each model tool round.
|
|
224
|
+
It receives a copy of that round's calls and returns a policy keyed by call ID. `dependsOn`
|
|
225
|
+
requires successful predecessor results before dispatch; failed predecessors produce skipped
|
|
226
|
+
receipts. Resource claims use a host-chosen asset identity shared across plugins: readers may
|
|
227
|
+
share a resource, while a writer excludes readers and other writers. Omitted claims mean unknown
|
|
228
|
+
shared state and serialize conservatively; `resources: []` explicitly permits independent work.
|
|
229
|
+
`maxConcurrency` bounds active calls, and `continueOnError: false` skips queued calls after a
|
|
230
|
+
failure while retaining already started outcomes. `mode: 'sequential'` limits dispatch to one call.
|
|
231
|
+
|
|
232
|
+
Claims are copied and validated before dispatch. Unknown IDs, cycles, and invalid resource claims
|
|
233
|
+
refuse the batch with paired skipped results. The policy grants no permissions and does not retry
|
|
234
|
+
mutations. Coordination is within one batch; hosts sharing assets across sessions must provide
|
|
235
|
+
coordination at their effect boundary. Callers of the lower-level batch service opt into resource
|
|
236
|
+
scheduling by supplying `scheduling`; its existing unplanned strategies retain their behavior.
|
|
237
|
+
|
|
223
238
|
## Entry points
|
|
224
239
|
|
|
225
240
|
| Import path | What it contains |
|
|
@@ -235,7 +250,7 @@ needs Node). Import them from `@robota-sdk/agent-core/node` only to pass them ex
|
|
|
235
250
|
|
|
236
251
|
| Area | Exports |
|
|
237
252
|
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
238
|
-
| **Agent** | `
|
|
253
|
+
| **Agent** | `ConversationAgent`, `ConversationStore`, `ConversationHistory`, `AbstractAgent`, `AgentFactory`, `AgentTemplates` |
|
|
239
254
|
| **Providers** | `AbstractAIProvider`, `IAIProvider`, `IChatOptions`, `IProviderCapabilities`, `getProviderCapabilities`, `assertProviderNativeWebToolsAvailable`, `IProviderDefinition`, `createProviderFromConfig` |
|
|
240
255
|
| **Media providers** | `IImageGenerationProvider`, `IVideoGenerationProvider`, `isImageGenerationProvider`, `isVideoGenerationProvider`, `createImageProviderFromDefinition`, `createVideoProviderFromDefinition`, `resolveMediaProviderConfig` |
|
|
241
256
|
| **Tools** | `FunctionTool`, `ToolRegistry`, `AbstractTool`, `IToolSchema`, `IToolResult`, `zodToJsonSchema` |
|
|
@@ -247,7 +262,7 @@ needs Node). Import them from `@robota-sdk/agent-core/node` only to pass them ex
|
|
|
247
262
|
| **Models** | `registerModelMetadata`, `getModelContextWindow`, `getModelMaxOutput`, `getModelName`, `formatTokenCount`, `DEFAULT_CONTEXT_WINDOW`, `DEFAULT_MAX_OUTPUT`, `IModelDefinition` |
|
|
248
263
|
| **Context and usage** | `estimateContextTokensFromMessages`, `estimateSerializedContextTokens`, `readTokenUsageFromMessage`, `sumMessagesUsage`, `IContextWindowState`, `IContextTokenUsage` |
|
|
249
264
|
| **Messages** | `TUniversalMessage`, `IBaseMessage`, `TMessageState`, `createUserMessage`, `createAssistantMessage`, `isAssistantMessage`, … |
|
|
250
|
-
| **Errors** | `
|
|
265
|
+
| **Errors** | `AgentRuntimeError`, `ProviderError`, `RateLimitError`, `AuthenticationError`, `ToolExecutionError`, `StructuredOutputError`, `classifyProviderFailure`, `toProviderError` |
|
|
251
266
|
|
|
252
267
|
## Where it sits
|
|
253
268
|
|
|
@@ -258,7 +273,7 @@ agent-tools, agent-session, agent-plugin, agent-mcp, agent-provider-* (one packa
|
|
|
258
273
|
↑
|
|
259
274
|
agent-framework ← assembles sessions, commands, permissions and hooks
|
|
260
275
|
↑
|
|
261
|
-
agent-cli ← the
|
|
276
|
+
agent-cli ← the reference host
|
|
262
277
|
```
|
|
263
278
|
|
|
264
279
|
- Built-in tools (Shell, Read, Edit, …): `@robota-sdk/agent-tools`
|
|
@@ -272,14 +287,33 @@ From `packages/agent-core` in a built checkout of the repository, these scripts
|
|
|
272
287
|
without a live model: `node examples/hook-block-demo.mjs`, `node examples/hook-json-response-demo.mjs`,
|
|
273
288
|
`node examples/hook-permission-mode-demo.mjs` and `node examples/hook-timeout-demo.mjs`.
|
|
274
289
|
|
|
290
|
+
The default outbound HTTP transport uses the installed Undici entry so Bun's built-in replacement
|
|
291
|
+
cannot discard its connection pinning. After `pnpm --filter @robota-sdk/agent-core build`, run the
|
|
292
|
+
same disposable HTTP fixture from the repository root in each runtime:
|
|
293
|
+
|
|
294
|
+
```sh
|
|
295
|
+
node packages/agent-core/src/utils/fixtures/egress-runtime-smoke.mjs
|
|
296
|
+
bun packages/agent-core/src/utils/fixtures/egress-runtime-smoke.mjs
|
|
297
|
+
egress_fixture_dir=$(mktemp -d)
|
|
298
|
+
bun build --compile --minify packages/agent-core/src/utils/fixtures/egress-runtime-smoke.mjs --outfile "$egress_fixture_dir/egress-smoke"
|
|
299
|
+
"$egress_fixture_dir/egress-smoke"
|
|
300
|
+
rm -rf "$egress_fixture_dir"
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
The fixture explicitly permits its local server and connects an otherwise unresolvable hostname
|
|
304
|
+
to an injected address. It checks the built helper and Bun compilation, not a full CLI session,
|
|
305
|
+
provider isolation or physical worker egress. The socket and TLS test suites cover destination
|
|
306
|
+
refusal, redirects, cleanup and certificate identity separately. A trusted injected `fetch` owns
|
|
307
|
+
its own connection policy; it does not inherit the default transport's pinning guarantee.
|
|
308
|
+
|
|
275
309
|
## Documentation
|
|
276
310
|
|
|
277
311
|
- [docs/SPEC.md](./docs/SPEC.md) — package contract and design decisions
|
|
278
312
|
- [docs/HOOK-CATALOG.md](./docs/HOOK-CATALOG.md) — hook events, input fields and blocking semantics
|
|
279
|
-
- [Building agents](../../content/guide/building-agents.md) — a walkthrough of `
|
|
313
|
+
- [Building agents](../../content/guide/building-agents.md) — a walkthrough of `ConversationAgent`, tools and
|
|
280
314
|
structured output
|
|
281
315
|
- [Permissions and hooks](../../content/guide/permissions-and-hooks.md)
|
|
282
316
|
|
|
283
317
|
## License
|
|
284
318
|
|
|
285
|
-
|
|
319
|
+
This package is dual-licensed under the [GNU AGPL-3.0](../../LICENSE) or a [commercial license](../../COMMERCIAL.md). See [LICENSING.md](../../LICENSING.md).
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=class extends Error{context;constructor(e,t){super(e),this.context=t,this.name=this.constructor.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{code=`CONFIGURATION_ERROR`;category=`user`;recoverable=!1;constructor(e,t){super(`Configuration Error: ${e}`,t)}},n=class extends e{field;code=`VALIDATION_ERROR`;category=`user`;recoverable=!1;constructor(e,t,n){super(`Validation Error: ${e}`,n),this.field=t}},r=class extends e{issues;attempts;code=`STRUCTURED_OUTPUT_ERROR`;category=`provider`;recoverable=!0;constructor(e,t,n,r){super(`Structured Output Error: ${e}`,r),this.issues=t,this.attempts=n}},i=class extends e{provider;originalError;code=`PROVIDER_ERROR`;category=`provider`;recoverable=!0;status;type;constructor(e,t,n,r,i){super(`Provider Error (${t}): ${e}`,r),this.provider=t,this.originalError=n,i?.status!==void 0&&(this.status=i.status),i?.type!==void 0&&(this.type=i.type)}},a=class extends e{provider;code=`AUTHENTICATION_ERROR`;category=`user`;recoverable=!1;constructor(e,t,n){super(`Authentication Error: ${e}`,n),this.provider=t}},o=class extends e{retryAfter;provider;code=`RATE_LIMIT_ERROR`;category=`provider`;recoverable=!0;constructor(e,t,n,r){super(`Rate Limit Error: ${e}`,r),this.retryAfter=t,this.provider=n}},s=class extends e{originalError;provider;code=`NETWORK_ERROR`;category=`system`;recoverable=!0;constructor(e,t,n,r){super(`Network Error: ${e}`,n),this.originalError=t,this.provider=r}},c=class extends e{toolName;originalError;code=`TOOL_EXECUTION_ERROR`;category=`system`;recoverable=!1;constructor(e,t,n,r){super(`Tool Execution Error (${t}): ${e}`,r),this.toolName=t,this.originalError=n}},l=class extends e{toolName;callCount;maxSameToolInputs;code=`SAME_TOOL_INPUT_LOOP`;category=`system`;recoverable=!0;constructor(e,t,n,r){super(`Tool "${e}" was called with identical input ${t} times, past the maxSameToolInputs limit of ${n} — stopping the turn to break the loop`,r),this.toolName=e,this.callCount=t,this.maxSameToolInputs=n}},u=class extends e{round;code=`EMPTY_COMPLETION`;category=`provider`;recoverable=!0;constructor(e,t){super(`Round ${e} ended with no tool call and no text response, and allowToolOnlyCompletion prevented the follow-up call that would otherwise recover it`,t),this.round=e}},d=class extends e{model;provider;availableModels;code=`MODEL_NOT_AVAILABLE`;category=`user`;recoverable=!1;constructor(e,t,n,r){let i=e===void 0?`No model available for provider "${t}"`:`Model "${e}" is not available for provider "${t}"`,a=r?.originalMessage;super(typeof a==`string`&&a.length>0?`${i}: ${a}`:i,r),this.model=e,this.provider=t,this.availableModels=n}},f=class extends e{code=`CIRCUIT_BREAKER_OPEN`;category=`system`;recoverable=!0;constructor(e=`Circuit breaker is open`,t){super(e,t)}},p=class extends e{pluginName;code=`PLUGIN_ERROR`;category=`system`;recoverable=!1;constructor(e,t,n){super(`Plugin Error (${t}): ${e}`,n),this.pluginName=t}},m=class extends e{code=`STORAGE_ERROR`;category=`system`;recoverable=!0;constructor(e,t){super(`Storage Error: ${e}`,t)}},h=class extends e{code=`CACHE_INTEGRITY_ERROR`;category=`system`;recoverable=!1;constructor(e,t){super(`Cache Integrity Error: ${e}`,t)}},g=class extends e{executable;code=`UNSUPPORTED_SHELL`;category=`user`;recoverable=!1;constructor(e){super(`Unsupported shell executable: ${e}`,{executable:e}),this.executable=e}};function _(e){return e===`darwin`?'Host OS: macOS (POSIX, BSD userland). Use BSD-flavored utilities: `sed -i ""` needs an empty backup arg, `date`/`grep`/`stat` flags differ from GNU. Prefer portable POSIX flags; avoid Linux/GNU-only options.':e===`linux`?"Host OS: Linux (POSIX, usually GNU coreutils, but distro/user setup varies). Prefer portable POSIX flags; do not assume a specific distro, and probe with `command -v` before relying on a non-standard tool.":`Host OS: ${e}; active shell family: POSIX sh/bash. Write portable POSIX syntax and avoid host-specific flags.`}function v(e,t){let n=x(e)===`bash`?`bash`:`sh`;return{command:e,kind:n,platform:t,commandArgs:e=>[`-c`,e],interactiveArgs:[],label:`${n} on ${t}`,syntaxHint:_(t)}}function y(e,t){return{command:e,kind:`powershell`,platform:t,commandArgs:e=>[`-NoProfile`,`-Command`,e],interactiveArgs:[`-NoProfile`],label:`PowerShell on ${t}`,syntaxHint:`Host OS: ${t}; active shell family: PowerShell. Write PowerShell syntax (not bash): e.g. \`Get-ChildItem\` not \`ls -la\`, \`\$env:VAR\` not \`\$VAR\`.`}}function b(e,t){return{command:e,kind:`cmd`,platform:t,commandArgs:e=>[`/d`,`/s`,`/c`,e],interactiveArgs:[],label:`cmd.exe on ${t}`,syntaxHint:`Host OS: ${t}; active shell family: Windows cmd.exe. Write cmd.exe syntax.`}}function x(e){return(e.split(/[\\/]/).at(-1)?.toLowerCase()??``).replace(/\.exe$/i,``)}function S(e,t){switch(x(e)){case`sh`:case`bash`:return v(e,t);case`powershell`:case`pwsh`:return y(e,t);case`cmd`:return b(e,t);default:throw new g(e)}}function C(e){let t=e?.trim();return t===void 0||t.length===0?void 0:t}function w(e={}){let t=e.env??process.env,n=e.platform??process.platform,r=C(e.executable);return r===void 0?n===`win32`?y(`powershell.exe`,n):v(C(t.SHELL)??`/bin/sh`,n):S(r,n)}const T=/^(?!0{32}$)[0-9a-f]{32}$/,E=/^(?!0{16}$)[0-9a-f]{16}$/;function D(e){return e.replaceAll(`-`,``).slice(0,16)}const O=D;function k(e){return E.test(e)}function A(e,t){if(!(!T.test(e)||!E.test(t)))return`00-${e}-${t}-01`}function j(e,t){if(e.allowedOrigins.length===0)return;let n=A(e.traceId,D(t));if(n!==void 0)return{traceparent:n,allowedOrigins:[...e.allowedOrigins]}}function M(e,t){return j(e,t)}function N(e,t,n){if(t?.subprocessClasses?.includes(e)!==!0)return;let r=A(t.traceId,n);return r===void 0?void 0:{TRACEPARENT:r}}function P(e,t,n={}){if(t===void 0||n.TRACEPARENT!==void 0)return{...e,...n};let{TRACESTATE:r,...i}=e;return{...i,...t,...n}}function F(e,t){if(e===void 0||t===void 0)return{};let n;try{n=new URL(e).origin}catch{return{}}return n===`null`||!t.allowedOrigins.includes(n)?{}:{traceparent:t.traceparent}}const I=e=>`\n…[output truncated: ${e} byte(s) dropped]`;function L(e){let t=e.retain??`head`,n=e.truncationMarker??I,r=Math.max(0,Math.floor(e.maxBytes)),i=new TextEncoder,a=[],o=0,s=0,c=e=>{let t=r-o;if(t<=0)return;let n=e.byteLength<=t?e:e.subarray(0,t);a.push(n),o+=n.byteLength},l=e=>{let t=e.byteLength<=r?e:e.subarray(e.byteLength-r);for(a.push(t),o+=t.byteLength;o>r&&a.length>0;){let e=a[0],t=o-r;e.byteLength<=t?(a.shift(),o-=e.byteLength):(a[0]=e.subarray(t),o-=t)}};return{append(e){let n=typeof e==`string`?i.encode(e):e;s+=n.byteLength,t===`head`?c(n):l(n)},get retainedBytes(){return o},get totalBytes(){return s},get droppedBytes(){return s-o},get truncated(){return s>o},toString(){let e=new Uint8Array(o),r=0;for(let t of a)e.set(t,r),r+=t.byteLength;a=a.length>1?[e]:a;let i=new TextDecoder().decode(e);if(s<=o)return i;let c=n(s-o);return t===`head`?`${i}${c}`:`${c.replace(/^\n/,``)}\n${i}`}}}export{
|
|
2
|
-
//# sourceMappingURL=bounded-output-
|
|
1
|
+
var e=class extends Error{context;constructor(e,t){super(e),this.context=t,this.name=this.constructor.name,Object.setPrototypeOf(this,new.target.prototype)}},t=class extends e{code=`CONFIGURATION_ERROR`;category=`user`;recoverable=!1;constructor(e,t){super(`Configuration Error: ${e}`,t)}},n=class extends e{field;code=`VALIDATION_ERROR`;category=`user`;recoverable=!1;constructor(e,t,n){super(`Validation Error: ${e}`,n),this.field=t}},r=class extends e{issues;attempts;code=`STRUCTURED_OUTPUT_ERROR`;category=`provider`;recoverable=!0;constructor(e,t,n,r){super(`Structured Output Error: ${e}`,r),this.issues=t,this.attempts=n}},i=class extends e{provider;originalError;code=`PROVIDER_ERROR`;category=`provider`;recoverable=!0;status;type;constructor(e,t,n,r,i){super(`Provider Error (${t}): ${e}`,r),this.provider=t,this.originalError=n,i?.status!==void 0&&(this.status=i.status),i?.type!==void 0&&(this.type=i.type)}},a=class extends e{provider;code=`AUTHENTICATION_ERROR`;category=`user`;recoverable=!1;constructor(e,t,n){super(`Authentication Error: ${e}`,n),this.provider=t}},o=class extends e{retryAfter;provider;code=`RATE_LIMIT_ERROR`;category=`provider`;recoverable=!0;constructor(e,t,n,r){super(`Rate Limit Error: ${e}`,r),this.retryAfter=t,this.provider=n}},s=class extends e{originalError;provider;code=`NETWORK_ERROR`;category=`system`;recoverable=!0;constructor(e,t,n,r){super(`Network Error: ${e}`,n),this.originalError=t,this.provider=r}},c=class extends e{toolName;originalError;code=`TOOL_EXECUTION_ERROR`;category=`system`;recoverable=!1;constructor(e,t,n,r){super(`Tool Execution Error (${t}): ${e}`,r),this.toolName=t,this.originalError=n}},l=class extends e{toolName;callCount;maxSameToolInputs;code=`SAME_TOOL_INPUT_LOOP`;category=`system`;recoverable=!0;constructor(e,t,n,r){super(`Tool "${e}" was called with identical input ${t} times, past the maxSameToolInputs limit of ${n} — stopping the turn to break the loop`,r),this.toolName=e,this.callCount=t,this.maxSameToolInputs=n}},u=class extends e{round;code=`EMPTY_COMPLETION`;category=`provider`;recoverable=!0;constructor(e,t){super(`Round ${e} ended with no tool call and no text response, and allowToolOnlyCompletion prevented the follow-up call that would otherwise recover it`,t),this.round=e}},d=class extends e{model;provider;availableModels;code=`MODEL_NOT_AVAILABLE`;category=`user`;recoverable=!1;constructor(e,t,n,r){let i=e===void 0?`No model available for provider "${t}"`:`Model "${e}" is not available for provider "${t}"`,a=r?.originalMessage;super(typeof a==`string`&&a.length>0?`${i}: ${a}`:i,r),this.model=e,this.provider=t,this.availableModels=n}},f=class extends e{code=`CIRCUIT_BREAKER_OPEN`;category=`system`;recoverable=!0;constructor(e=`Circuit breaker is open`,t){super(e,t)}},p=class extends e{pluginName;code=`PLUGIN_ERROR`;category=`system`;recoverable=!1;constructor(e,t,n){super(`Plugin Error (${t}): ${e}`,n),this.pluginName=t}},m=class extends e{code=`STORAGE_ERROR`;category=`system`;recoverable=!0;constructor(e,t){super(`Storage Error: ${e}`,t)}},h=class extends e{code=`CACHE_INTEGRITY_ERROR`;category=`system`;recoverable=!1;constructor(e,t){super(`Cache Integrity Error: ${e}`,t)}},g=class extends e{executable;code=`UNSUPPORTED_SHELL`;category=`user`;recoverable=!1;constructor(e){super(`Unsupported shell executable: ${e}`,{executable:e}),this.executable=e}};function _(e){return e===`darwin`?'Host OS: macOS (POSIX, BSD userland). Use BSD-flavored utilities: `sed -i ""` needs an empty backup arg, `date`/`grep`/`stat` flags differ from GNU. Prefer portable POSIX flags; avoid Linux/GNU-only options.':e===`linux`?"Host OS: Linux (POSIX, usually GNU coreutils, but distro/user setup varies). Prefer portable POSIX flags; do not assume a specific distro, and probe with `command -v` before relying on a non-standard tool.":`Host OS: ${e}; active shell family: POSIX sh/bash. Write portable POSIX syntax and avoid host-specific flags.`}function v(e,t){let n=x(e)===`bash`?`bash`:`sh`;return{command:e,kind:n,platform:t,commandArgs:e=>[`-c`,e],interactiveArgs:[],label:`${n} on ${t}`,syntaxHint:_(t)}}function y(e,t){return{command:e,kind:`powershell`,platform:t,commandArgs:e=>[`-NoProfile`,`-Command`,e],interactiveArgs:[`-NoProfile`],label:`PowerShell on ${t}`,syntaxHint:`Host OS: ${t}; active shell family: PowerShell. Write PowerShell syntax (not bash): e.g. \`Get-ChildItem\` not \`ls -la\`, \`\$env:VAR\` not \`\$VAR\`.`}}function b(e,t){return{command:e,kind:`cmd`,platform:t,commandArgs:e=>[`/d`,`/s`,`/c`,e],interactiveArgs:[],label:`cmd.exe on ${t}`,syntaxHint:`Host OS: ${t}; active shell family: Windows cmd.exe. Write cmd.exe syntax.`}}function x(e){return(e.split(/[\\/]/).at(-1)?.toLowerCase()??``).replace(/\.exe$/i,``)}function S(e,t){switch(x(e)){case`sh`:case`bash`:return v(e,t);case`powershell`:case`pwsh`:return y(e,t);case`cmd`:return b(e,t);default:throw new g(e)}}function C(e){let t=e?.trim();return t===void 0||t.length===0?void 0:t}function w(e={}){let t=e.env??process.env,n=e.platform??process.platform,r=C(e.executable);return r===void 0?n===`win32`?y(`powershell.exe`,n):v(C(t.SHELL)??`/bin/sh`,n):S(r,n)}const T=/^(?!0{32}$)[0-9a-f]{32}$/,E=/^(?!0{16}$)[0-9a-f]{16}$/;function D(e){return e.replaceAll(`-`,``).slice(0,16)}const O=D;function k(e){return E.test(e)}function A(e,t){if(!(!T.test(e)||!E.test(t)))return`00-${e}-${t}-01`}function j(e,t){if(e.allowedOrigins.length===0)return;let n=A(e.traceId,D(t));if(n!==void 0)return{traceparent:n,allowedOrigins:[...e.allowedOrigins]}}function M(e,t){return j(e,t)}function N(e,t,n){if(t?.subprocessClasses?.includes(e)!==!0)return;let r=A(t.traceId,n);return r===void 0?void 0:{TRACEPARENT:r}}function P(e,t,n={}){if(t===void 0||n.TRACEPARENT!==void 0)return{...e,...n};let{TRACESTATE:r,...i}=e;return{...i,...t,...n}}function F(e,t){if(e===void 0||t===void 0)return{};let n;try{n=new URL(e).origin}catch{return{}}return n===`null`||!t.allowedOrigins.includes(n)?{}:{traceparent:t.traceparent}}const I=e=>`\n…[output truncated: ${e} byte(s) dropped]`;function L(e){let t=e.retain??`head`,n=e.truncationMarker??I,r=Math.max(0,Math.floor(e.maxBytes)),i=new TextEncoder,a=[],o=0,s=0,c=e=>{let t=r-o;if(t<=0)return;let n=e.byteLength<=t?e:e.subarray(0,t);a.push(n),o+=n.byteLength},l=e=>{let t=e.byteLength<=r?e:e.subarray(e.byteLength-r);for(a.push(t),o+=t.byteLength;o>r&&a.length>0;){let e=a[0],t=o-r;e.byteLength<=t?(a.shift(),o-=e.byteLength):(a[0]=e.subarray(t),o-=t)}};return{append(e){let n=typeof e==`string`?i.encode(e):e;s+=n.byteLength,t===`head`?c(n):l(n)},get retainedBytes(){return o},get totalBytes(){return s},get droppedBytes(){return s-o},get truncated(){return s>o},toString(){let e=new Uint8Array(o),r=0;for(let t of a)e.set(t,r),r+=t.byteLength;a=a.length>1?[e]:a;let i=new TextDecoder().decode(e);if(s<=o)return i;let c=n(s-o);return t===`head`?`${i}${c}`:`${c.replace(/^\n/,``)}\n${i}`}}}export{o as C,c as D,r as E,n as O,i as S,m as T,t as _,O as a,s as b,M as c,g as d,w as f,f as g,h,j as i,N as l,a as m,A as n,D as o,e as p,k as r,P as s,L as t,F as u,u as v,l as w,p as x,d as y};
|
|
2
|
+
//# sourceMappingURL=bounded-output-BdWv9SX3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bounded-output-BdWv9SX3.js","names":[],"sources":["../../src/utils/errors.ts","../../src/utils/platform-shell.ts","../../src/utils/trace-context.ts","../../src/utils/bounded-output.ts"],"sourcesContent":["/**\n * Reusable type definitions for error utilities\n */\n\n/**\n * Error context data type\n * Used for storing contextual information in error instances\n */\nexport type TErrorContextData = Record<\n string,\n string | number | boolean | Date | Error | string[] | undefined\n>;\n\n/**\n * Error external input type\n * Used for handling external errors from unknown sources\n */\nexport type TErrorExternalInput =\n Error | string | Record<string, string | number | boolean> | null | undefined;\n\n/**\n * Base error class for all ConversationAgent errors\n */\nexport abstract class AgentRuntimeError extends Error {\n abstract readonly code: string;\n abstract readonly category: 'user' | 'system' | 'provider';\n abstract readonly recoverable: boolean;\n\n constructor(\n message: string,\n public readonly context?: TErrorContextData,\n ) {\n super(message);\n this.name = this.constructor.name;\n\n // Ensure proper prototype chain for instanceof checks\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Configuration related errors\n */\nexport class ConfigurationError extends AgentRuntimeError {\n readonly code = 'CONFIGURATION_ERROR';\n readonly category = 'user' as const;\n readonly recoverable = false;\n\n constructor(message: string, context?: TErrorContextData) {\n super(`Configuration Error: ${message}`, context);\n }\n}\n\n/**\n * Input validation errors\n */\nexport class ValidationError extends AgentRuntimeError {\n readonly code = 'VALIDATION_ERROR';\n readonly category = 'user' as const;\n readonly recoverable = false;\n\n constructor(\n message: string,\n public readonly field?: string,\n context?: TErrorContextData,\n ) {\n super(`Validation Error: ${message}`, context);\n }\n}\n\n/**\n * Structured output validation exhausted its retry budget (CORE-015).\n *\n * Thrown by `run(input, { output })` when the model's final response still fails\n * schema validation after the configured number of retries. `issues` holds the\n * validation messages from the last attempt; `attempts` is the total number of\n * provider turns spent (initial + retries).\n */\nexport class StructuredOutputError extends AgentRuntimeError {\n readonly code = 'STRUCTURED_OUTPUT_ERROR';\n readonly category = 'provider' as const;\n readonly recoverable = true;\n\n constructor(\n message: string,\n public readonly issues: string[],\n public readonly attempts: number,\n context?: TErrorContextData,\n ) {\n super(`Structured Output Error: ${message}`, context);\n }\n}\n\n/** What the vendor said about a failed call, as far as it said anything. */\nexport interface IProviderFailureDetails {\n /** The HTTP status of the failed response; absent for a failure with no response (e.g. an SSE error event). */\n status?: number;\n /** The vendor's own error type, e.g. Anthropic's `overloaded_error`. */\n type?: string;\n}\n\n/**\n * Provider related errors\n */\nexport class ProviderError extends AgentRuntimeError {\n readonly code = 'PROVIDER_ERROR';\n readonly category = 'provider' as const;\n readonly recoverable = true;\n readonly status?: number;\n readonly type?: string;\n\n constructor(\n message: string,\n public readonly provider: string,\n public readonly originalError?: Error,\n context?: TErrorContextData,\n details?: IProviderFailureDetails,\n ) {\n super(`Provider Error (${provider}): ${message}`, context);\n if (details?.status !== undefined) this.status = details.status;\n if (details?.type !== undefined) this.type = details.type;\n }\n}\n\n/**\n * Authentication errors\n */\nexport class AuthenticationError extends AgentRuntimeError {\n readonly code = 'AUTHENTICATION_ERROR';\n readonly category = 'user' as const;\n readonly recoverable = false;\n\n constructor(\n message: string,\n public readonly provider?: string,\n context?: TErrorContextData,\n ) {\n super(`Authentication Error: ${message}`, context);\n }\n}\n\n/**\n * Rate limit errors\n */\nexport class RateLimitError extends AgentRuntimeError {\n readonly code = 'RATE_LIMIT_ERROR';\n readonly category = 'provider' as const;\n readonly recoverable = true;\n\n constructor(\n message: string,\n public readonly retryAfter?: number,\n public readonly provider?: string,\n context?: TErrorContextData,\n ) {\n super(`Rate Limit Error: ${message}`, context);\n }\n}\n\n/**\n * Network/connectivity errors\n */\nexport class NetworkError extends AgentRuntimeError {\n readonly code = 'NETWORK_ERROR';\n readonly category = 'system' as const;\n readonly recoverable = true;\n\n constructor(\n message: string,\n public readonly originalError?: Error,\n context?: TErrorContextData,\n /** `undefined` for a network failure with no provider context (e.g. a generic timeout). */\n public readonly provider?: string,\n ) {\n super(`Network Error: ${message}`, context);\n }\n}\n\n/**\n * Tool execution errors\n */\nexport class ToolExecutionError extends AgentRuntimeError {\n readonly code = 'TOOL_EXECUTION_ERROR';\n readonly category = 'system' as const;\n readonly recoverable = false;\n\n constructor(\n message: string,\n public readonly toolName: string,\n public readonly originalError?: Error,\n context?: TErrorContextData,\n ) {\n super(`Tool Execution Error (${toolName}): ${message}`, context);\n }\n}\n\n/**\n * The same-tool-input loop guard tripped — CORE-035.\n *\n * A tool was invoked with byte-identical serialized inputs more times than `maxSameToolInputs`\n * allows, so the turn stopped rather than looping. This is the AGENT giving up, not the user\n * cancelling: the SPEC used to promise an `AbortError` here, which `isAbortFailure` resolves as\n * `success: true, interrupted: true` — reporting a run that produced no answer as a success.\n *\n * Named rather than a bare `Error` for the reason the SPEC was reaching for by naming a type at all:\n * a caller must be able to tell \"the agent looped\" from \"the network died\". `AgentRuntimeError` carries\n * `code`/`category`/`recoverable` out through CORE-027's failure path intact.\n *\n * `recoverable` is TRUE: the loop is a property of this turn's prompt and tool set, not of the\n * system, and a caller that varies either can reasonably try again.\n */\nexport class SameToolInputLoopError extends AgentRuntimeError {\n readonly code = 'SAME_TOOL_INPUT_LOOP';\n readonly category = 'system' as const;\n readonly recoverable = true;\n\n constructor(\n public readonly toolName: string,\n public readonly callCount: number,\n public readonly maxSameToolInputs: number,\n context?: TErrorContextData,\n ) {\n super(\n `Tool \"${toolName}\" was called with identical input ${callCount} times, past the ` +\n `maxSameToolInputs limit of ${maxSameToolInputs} — stopping the turn to break the loop`,\n context,\n );\n }\n}\n\n/**\n * A turn ended with neither a tool result nor a final text response while `allowToolOnlyCompletion`\n * was set.\n *\n * `allowToolOnlyCompletion` only WIDENS what counts as a valid finish (a tool call alone, with no\n * final text after it); it never narrows the ordinary case. Without the flag, a round that produces\n * neither is recovered by one more provider call asking for a summary (`forceSummaryCall`). Setting\n * the flag is what turns that rescue off — a decision agent wants exactly one provider call, never a\n * follow-up — so a round that produces neither has nothing left to recover it. That is a genuinely\n * unanswered turn, not the internal `[STRICT-POLICY]` result-shape violation an actually malformed\n * execution result trips; a caller must be able to catch this by type instead of parsing that message.\n *\n * `recoverable` is TRUE: like `SameToolInputLoopError`, this is a property of this turn's prompt and\n * model response, not of the system, and a caller that varies either can reasonably try again.\n */\nexport class EmptyCompletionError extends AgentRuntimeError {\n readonly code = 'EMPTY_COMPLETION';\n readonly category = 'provider' as const;\n readonly recoverable = true;\n\n constructor(\n public readonly round: number,\n context?: TErrorContextData,\n ) {\n super(\n `Round ${round} ended with no tool call and no text response, and allowToolOnlyCompletion ` +\n 'prevented the follow-up call that would otherwise recover it',\n context,\n );\n }\n}\n\n/**\n * Model not available errors\n */\nexport class ModelNotAvailableError extends AgentRuntimeError {\n readonly code = 'MODEL_NOT_AVAILABLE';\n readonly category = 'user' as const;\n readonly recoverable = false;\n\n constructor(\n /** `undefined` when the failure names no model — a provider adapter classifying a raw HTTP\n * response by status alone does not always know which model the vendor meant. */\n public readonly model: string | undefined,\n public readonly provider: string,\n public readonly availableModels?: string[],\n /**\n * `context.originalMessage`, when given, is the vendor's own text (already scrubbed of any\n * credential by the caller) — folded into `message` so a \"Details\" disclosure built from\n * `message` alone still shows the real cause instead of just this class's fixed sentence.\n */\n context?: TErrorContextData,\n ) {\n const base =\n model !== undefined\n ? `Model \"${model}\" is not available for provider \"${provider}\"`\n : `No model available for provider \"${provider}\"`;\n const detail = context?.['originalMessage'];\n super(typeof detail === 'string' && detail.length > 0 ? `${base}: ${detail}` : base, context);\n }\n}\n\n/**\n * Circuit breaker open error\n */\nexport class CircuitBreakerOpenError extends AgentRuntimeError {\n readonly code = 'CIRCUIT_BREAKER_OPEN';\n readonly category = 'system' as const;\n readonly recoverable = true;\n\n constructor(message: string = 'Circuit breaker is open', context?: TErrorContextData) {\n super(message, context);\n }\n}\n\n/**\n * Plugin errors\n */\nexport class PluginError extends AgentRuntimeError {\n readonly code = 'PLUGIN_ERROR';\n readonly category = 'system' as const;\n readonly recoverable = false;\n\n constructor(\n message: string,\n public readonly pluginName: string,\n context?: TErrorContextData,\n ) {\n super(`Plugin Error (${pluginName}): ${message}`, context);\n }\n}\n\n/**\n * Storage related errors\n */\nexport class StorageError extends AgentRuntimeError {\n readonly code = 'STORAGE_ERROR';\n readonly category = 'system' as const;\n readonly recoverable = true;\n\n constructor(message: string, context?: TErrorContextData) {\n super(`Storage Error: ${message}`, context);\n }\n}\n\n/**\n * Cache integrity validation errors\n */\nexport class CacheIntegrityError extends AgentRuntimeError {\n readonly code = 'CACHE_INTEGRITY_ERROR';\n readonly category = 'system' as const;\n readonly recoverable = false;\n\n constructor(message: string, context?: TErrorContextData) {\n super(`Cache Integrity Error: ${message}`, context);\n }\n}\n","/**\n * TERM-008: cross-platform shell resolution (SSOT).\n *\n * Single source of truth for \"which shell do we spawn, and how\", shared by every shell-running site.\n * Resolution is a pure function of one request so every branch is testable without the host shell.\n */\n\nimport { AgentRuntimeError } from './errors.js';\n\n/** Shell family — drives non-interactive arg shape, quoting, and LLM syntax guidance. */\nexport type TShellKind = 'bash' | 'sh' | 'powershell' | 'cmd';\n\n/** The active shell resolved for a platform, plus the metadata callers need to drive and describe it. */\nexport interface IPlatformShell {\n /** Executable to spawn (e.g. `/bin/sh`, `powershell.exe`, or `$SHELL`). */\n readonly command: string;\n /** Shell family. */\n readonly kind: TShellKind;\n /** Node platform this was resolved for (`process.platform`). */\n readonly platform: NodeJS.Platform;\n /** Args to run a single command string non-interactively. */\n commandArgs(command: string): string[];\n /** Args for an interactive shell session (drop-to-shell). */\n readonly interactiveArgs: string[];\n /** Human label for the active shell, for tool/UI descriptions (e.g. `PowerShell (Windows)`). */\n readonly label: string;\n /** One-line syntax guidance for an LLM authoring commands. */\n readonly syntaxHint: string;\n}\n\n/** Pure inputs for resolving one executable together with its command argument family. */\nexport interface IPlatformShellResolutionRequest {\n /** Request-local executable. A non-blank value has the highest precedence. */\n readonly executable?: string;\n /** Environment used for SHELL resolution. */\n readonly env?: NodeJS.ProcessEnv;\n /** Actual host platform retained in the resolved metadata. */\n readonly platform?: NodeJS.Platform;\n}\n\n/** An explicit executable whose basename has no supported command argument family. */\nexport class UnsupportedShellError extends AgentRuntimeError {\n readonly code = 'UNSUPPORTED_SHELL';\n readonly category = 'user' as const;\n readonly recoverable = false;\n\n constructor(public readonly executable: string) {\n super(`Unsupported shell executable: ${executable}`, { executable });\n }\n}\n\n/**\n * OS-family syntax guidance for the LLM. macOS and Linux are both POSIX but ship different userlands\n * (macOS = BSD coreutils, most Linux = GNU coreutils), so flags like `sed -i`, `date`, and `grep`\n * differ — naming the OS lets the model avoid Linux-only invocations on macOS and vice versa.\n */\nfunction posixSyntaxHint(platform: NodeJS.Platform): string {\n if (platform === 'darwin') {\n return 'Host OS: macOS (POSIX, BSD userland). Use BSD-flavored utilities: `sed -i \"\"` needs an empty backup arg, `date`/`grep`/`stat` flags differ from GNU. Prefer portable POSIX flags; avoid Linux/GNU-only options.';\n }\n if (platform === 'linux') {\n return 'Host OS: Linux (POSIX, usually GNU coreutils, but distro/user setup varies). Prefer portable POSIX flags; do not assume a specific distro, and probe with `command -v` before relying on a non-standard tool.';\n }\n return `Host OS: ${platform}; active shell family: POSIX sh/bash. Write portable POSIX syntax and avoid host-specific flags.`;\n}\n\nfunction posixShell(command: string, platform: NodeJS.Platform): IPlatformShell {\n const isBash = shellBasename(command) === 'bash';\n const kind: TShellKind = isBash ? 'bash' : 'sh';\n return {\n command,\n kind,\n platform,\n commandArgs: (cmd: string): string[] => ['-c', cmd],\n interactiveArgs: [],\n label: `${kind} on ${platform}`,\n syntaxHint: posixSyntaxHint(platform),\n };\n}\n\nfunction powerShell(command: string, platform: NodeJS.Platform): IPlatformShell {\n return {\n command,\n kind: 'powershell',\n platform,\n commandArgs: (cmd: string): string[] => ['-NoProfile', '-Command', cmd],\n interactiveArgs: ['-NoProfile'],\n label: `PowerShell on ${platform}`,\n syntaxHint:\n `Host OS: ${platform}; active shell family: PowerShell. Write PowerShell syntax (not bash): e.g. ` +\n '`Get-ChildItem` not `ls -la`, `$env:VAR` not `$VAR`.',\n };\n}\n\nfunction cmdShell(command: string, platform: NodeJS.Platform): IPlatformShell {\n return {\n command,\n kind: 'cmd',\n platform,\n commandArgs: (cmd: string): string[] => ['/d', '/s', '/c', cmd],\n interactiveArgs: [],\n label: `cmd.exe on ${platform}`,\n syntaxHint: `Host OS: ${platform}; active shell family: Windows cmd.exe. Write cmd.exe syntax.`,\n };\n}\n\nfunction shellBasename(command: string): string {\n const basename = command.split(/[\\\\/]/).at(-1)?.toLowerCase() ?? '';\n return basename.replace(/\\.exe$/i, '');\n}\n\nfunction explicitShell(command: string, platform: NodeJS.Platform): IPlatformShell {\n switch (shellBasename(command)) {\n case 'sh':\n case 'bash':\n return posixShell(command, platform);\n case 'powershell':\n case 'pwsh':\n return powerShell(command, platform);\n case 'cmd':\n return cmdShell(command, platform);\n default:\n throw new UnsupportedShellError(command);\n }\n}\n\nfunction nonBlank(value: string | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed === undefined || trimmed.length === 0 ? undefined : trimmed;\n}\n\n/**\n * Resolve the shell to spawn for the current (or a given) platform.\n *\n * - request `executable` wins when non-blank.\n * - **win32:** PowerShell.\n * - **posix:** `$SHELL` if set, else `/bin/sh`.\n */\nexport function resolvePlatformShell(\n request: IPlatformShellResolutionRequest = {},\n): IPlatformShell {\n const env = request.env ?? process.env;\n const platform = request.platform ?? process.platform;\n const requestedExecutable = nonBlank(request.executable);\n if (requestedExecutable !== undefined) return explicitShell(requestedExecutable, platform);\n\n if (platform === 'win32') return powerShell('powershell.exe', platform);\n\n return posixShell(nonBlank(env['SHELL']) ?? '/bin/sh', platform);\n}\n","/**\n * Pure helpers for trusted W3C trace context. Strings and the WHATWG `URL` only, so they are the\n * same in the browser build and in every adapter that attaches the header.\n */\nimport type {\n IOutboundTraceContext,\n IRunTraceContext,\n ISubprocessTraceEnv,\n TSubprocessTraceClass,\n} from '../interfaces/trace-context';\n\nconst TRACE_ID = /^(?!0{32}$)[0-9a-f]{32}$/;\nconst SPAN_ID = /^(?!0{16}$)[0-9a-f]{16}$/;\n\n/**\n * The span ID an operation is exported under, derived from the ID core mints before it runs (a\n * provider call's call ID, a tool body's body ID). One derivation for both the exporter and the\n * outgoing header, so the span a recipient sees as its parent is the span the trace contains.\n */\nexport function spanIdFromMintedId(mintedId: string): string {\n return mintedId.replaceAll('-', '').slice(0, 16);\n}\n\n/** The provider-call name for {@link spanIdFromMintedId}, kept so existing callers keep working. */\nexport const providerCallSpanId: (callId: string) => string = spanIdFromMintedId;\n\n/** Whether `spanId` is a well-formed minted span ID: 16 lowercase hex characters, not all zero. */\nexport function isMintedSpanId(spanId: string): boolean {\n return SPAN_ID.test(spanId);\n}\n\n/** A sampled version-00 `traceparent`, or undefined when either identifier is invalid. */\nexport function buildTraceparent(traceId: string, spanId: string): string | undefined {\n if (!TRACE_ID.test(traceId) || !SPAN_ID.test(spanId)) return undefined;\n return `00-${traceId}-${spanId}-01`;\n}\n\n/** The outbound context for one invoked call, or undefined when there is nothing valid to send. */\nexport function outboundTraceContextFor(\n traceContext: IRunTraceContext,\n callId: string,\n): IOutboundTraceContext | undefined {\n if (traceContext.allowedOrigins.length === 0) return undefined;\n const traceparent = buildTraceparent(traceContext.traceId, spanIdFromMintedId(callId));\n if (traceparent === undefined) return undefined;\n return { traceparent, allowedOrigins: [...traceContext.allowedOrigins] };\n}\n\n/**\n * The outbound context for one tool body, keyed by the body ID core minted for it — never the\n * vendor's tool call ID, which is neither unique nor hex.\n */\nexport function toolTraceContextFor(\n traceContext: IRunTraceContext,\n toolBodyId: string,\n): IOutboundTraceContext | undefined {\n return outboundTraceContextFor(traceContext, toolBodyId);\n}\n\n/**\n * The environment one child process of `subprocessClass` receives, naming `spanId` as its parent,\n * or undefined when the host did not enable that class. Independent of the origin allowlist.\n */\nexport function traceEnvFor(\n subprocessClass: TSubprocessTraceClass,\n traceContext: IRunTraceContext | undefined,\n spanId: string,\n): ISubprocessTraceEnv | undefined {\n if (traceContext?.subprocessClasses?.includes(subprocessClass) !== true) return undefined;\n const traceparent = buildTraceparent(traceContext.traceId, spanId);\n return traceparent === undefined ? undefined : { TRACEPARENT: traceparent };\n}\n\n/**\n * A fresh child environment: `base`, then ConversationAgent's trace, then `overrides`. When ConversationAgent's value\n * applies, the ambient `TRACESTATE` is dropped because it belongs to a different parent. When\n * `overrides` sets its own `TRACEPARENT`, ConversationAgent's value does not apply and `base` passes unchanged.\n * `base` itself is never modified.\n */\nexport function subprocessTraceEnvironment(\n base: Readonly<Record<string, string | undefined>>,\n traceEnv: ISubprocessTraceEnv | undefined,\n overrides: Readonly<Record<string, string>> = {},\n): Record<string, string | undefined> {\n if (traceEnv === undefined || overrides['TRACEPARENT'] !== undefined) return { ...base, ...overrides };\n const { TRACESTATE: _ambientState, ...rest } = base;\n return { ...rest, ...traceEnv, ...overrides };\n}\n\n/**\n * The headers to add to a request whose effective base URL is `baseUrl`: `traceparent` when its\n * origin exactly equals a listed origin, nothing otherwise. Scheme, host and port must all match;\n * a subdomain or a different port is a different origin.\n */\nexport function traceHeadersFor(\n baseUrl: string | undefined,\n traceContext: IOutboundTraceContext | undefined,\n): Readonly<Record<string, string>> {\n if (baseUrl === undefined || traceContext === undefined) return {};\n let origin: string;\n try {\n origin = new URL(baseUrl).origin;\n } catch {\n return {};\n }\n if (origin === 'null' || !traceContext.allowedOrigins.includes(origin)) return {};\n return { traceparent: traceContext.traceparent };\n}\n","/**\n * ARCH-056 (#2161): the bounded output-retention contract for child-process execution.\n *\n * Every path that reads a child's stdout/stderr must bound memory WHILE the output is being read —\n * not cap a summary afterwards while retaining the whole log. Before this existed, `shell-tool`\n * could buffer for ten minutes, the hook `CommandExecutor` repeated the pattern, and a subagent\n * runner had rediscovered a 4 KiB tail on its own. This module names the policy once:\n *\n * - **Budget.** `maxBytes` is the most that is ever RETAINED; bytes beyond it are dropped as they\n * arrive, so memory is bounded by the budget, not by the child's lifetime.\n * - **Retention.** `head` keeps the first `maxBytes` (a tool result: what the command said first);\n * `tail` keeps the last `maxBytes` (a diagnostic: how it ended).\n * - **Truncation is visible.** `truncated`, `droppedBytes` and a marker in `toString()` say that\n * output was dropped and how much — never a silently shorter string.\n * - **No spill.** This contract never writes to disk; a caller that needs the whole stream owns a\n * log file (background tasks do), and this buffer is what stays in memory beside it.\n * - **Termination is not here.** Exceeding the budget drops bytes; whether the child is killed is\n * the caller's policy (CORE-023 owns process-tree termination).\n *\n * Chunks are `Uint8Array` (a Node `Buffer` is one), and decoding happens once at `toString()`, so a\n * multi-byte character split across a chunk boundary is never decoded twice.\n */\n\nexport type TOutputRetention = 'head' | 'tail';\n\nexport interface IBoundedOutputOptions {\n /** Most bytes ever retained. */\n readonly maxBytes: number;\n /** Which end to keep once over budget. Default `head`. */\n readonly retain?: TOutputRetention;\n /** Marker appended (head) or prepended (tail) to `toString()` when bytes were dropped. */\n readonly truncationMarker?: (droppedBytes: number) => string;\n}\n\nexport interface IBoundedOutput {\n append(chunk: Uint8Array | string): void;\n /** Bytes currently retained (≤ `maxBytes`). */\n readonly retainedBytes: number;\n /** Bytes the child produced in total, retained or not. */\n readonly totalBytes: number;\n readonly droppedBytes: number;\n readonly truncated: boolean;\n /** The retained output, decoded as UTF-8, with the truncation marker when bytes were dropped. */\n toString(): string;\n}\n\nconst DEFAULT_MARKER = (dropped: number): string =>\n `\\n…[output truncated: ${dropped} byte(s) dropped]`;\n\nexport function createBoundedOutput(options: IBoundedOutputOptions): IBoundedOutput {\n const retain = options.retain ?? 'head';\n const marker = options.truncationMarker ?? DEFAULT_MARKER;\n const maxBytes = Math.max(0, Math.floor(options.maxBytes));\n const encoder = new TextEncoder();\n let chunks: Uint8Array[] = [];\n let retained = 0;\n let total = 0;\n\n const appendHead = (bytes: Uint8Array): void => {\n const room = maxBytes - retained;\n if (room <= 0) return;\n const kept = bytes.byteLength <= room ? bytes : bytes.subarray(0, room);\n chunks.push(kept);\n retained += kept.byteLength;\n };\n\n const appendTail = (bytes: Uint8Array): void => {\n const kept = bytes.byteLength <= maxBytes ? bytes : bytes.subarray(bytes.byteLength - maxBytes);\n chunks.push(kept);\n retained += kept.byteLength;\n // Drop whole leading chunks first, then trim the oldest survivor — O(chunks), no re-copying.\n while (retained > maxBytes && chunks.length > 0) {\n const oldest = chunks[0] as Uint8Array;\n const excess = retained - maxBytes;\n if (oldest.byteLength <= excess) {\n chunks.shift();\n retained -= oldest.byteLength;\n } else {\n chunks[0] = oldest.subarray(excess);\n retained -= excess;\n }\n }\n };\n\n return {\n append(chunk) {\n const bytes = typeof chunk === 'string' ? encoder.encode(chunk) : chunk;\n total += bytes.byteLength;\n if (retain === 'head') appendHead(bytes);\n else appendTail(bytes);\n },\n get retainedBytes() {\n return retained;\n },\n get totalBytes() {\n return total;\n },\n get droppedBytes() {\n return total - retained;\n },\n get truncated() {\n return total > retained;\n },\n toString() {\n const joined = new Uint8Array(retained);\n let offset = 0;\n for (const chunk of chunks) {\n joined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n chunks = chunks.length > 1 ? [joined] : chunks;\n const text = new TextDecoder().decode(joined);\n if (total <= retained) return text;\n const note = marker(total - retained);\n return retain === 'head' ? `${text}${note}` : `${note.replace(/^\\n/, '')}\\n${text}`;\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{f as e,s as t,t as n}from"./bounded-output-
|
|
2
|
-
//# sourceMappingURL=command-executor-
|
|
1
|
+
import{f as e,s as t,t as n}from"./bounded-output-BdWv9SX3.js";import{spawn as r}from"node:child_process";const i=1e6;function a(e,t){let r=n({maxBytes:i}),a=n({maxBytes:i,retain:`tail`});return e.stdout?.on(`data`,e=>r.append(e)),e.stderr?.on(`data`,e=>a.append(e)),e.stdin?.on(`error`,()=>{}),e.stdin?.write(t),e.stdin?.end(),{stdoutOutput:r,stderrOutput:a}}function o(e,t,n,r){if(e===0)return{outcome:`allow`,source:`command`,stdout:n};if(e===2)return{outcome:`deny`,source:`command`,reason:r.trim()||`Blocked by hook`};let i=r.trim()?`: ${r.trim()}`:``;return{outcome:`error`,source:`command`,kind:`nonzero-exit`,reason:e===null?`Hook terminated by signal ${t??`unknown`}${i}`:`Hook exited ${e}${i}`}}var s=class{shellExecutable;type=`command`;constructor(e){this.shellExecutable=e}execute(n,i,s){let c=n.timeout??600,l=c*1e3,u=JSON.stringify(i);return new Promise(d=>{let f=!1,p=e({executable:this.shellExecutable}),m=r(p.command,p.commandArgs(n.command),{cwd:i.cwd,env:t(process.env,s,i.env)}),{stdoutOutput:h,stderrOutput:g}=a(m,u),_=setTimeout(()=>{f||(f=!0,m.kill(`SIGTERM`),d({outcome:`error`,source:`command`,kind:`timeout`,reason:`Hook timed out after ${c}s`}))},l);m.on(`close`,(e,t)=>{f||(f=!0,clearTimeout(_),d(o(e,t,h.toString(),g.toString())))}),m.on(`error`,e=>{f||(f=!0,clearTimeout(_),d({outcome:`error`,source:`command`,kind:`spawn-failure`,reason:e.message}))})})}};export{s as CommandExecutor};
|
|
2
|
+
//# sourceMappingURL=command-executor-DIXaSMrr.js.map
|
package/dist/{node/command-executor-d6DdfUTD.js.map → browser/command-executor-DIXaSMrr.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-executor-
|
|
1
|
+
{"version":3,"file":"command-executor-DIXaSMrr.js","names":[],"sources":["../../src/hooks/executors/command-executor.ts"],"sourcesContent":["/**\n * Command hook executor — executes shell commands with JSON input on stdin.\n *\n * Exit-code mapping (SEC-015):\n * - `0` → `allow`, stdout carried for the runner's response-protocol decode\n * - `2` → `deny`, stderr carried as the reason\n * - any other code → `error` / `nonzero-exit`\n * - killed by a signal (exit code `null`) → `error` / `nonzero-exit`, signal named in the reason\n * - deadline elapsed → `error` / `timeout`\n * - the process never started → `error` / `spawn-failure`\n *\n * The last four used to be one number. A missing binary exits `127` and a timeout resolved `1`, and\n * the runner discarded both alike, so a gate that never ran was indistinguishable from one that\n * approved.\n */\n\nimport { spawn } from 'node:child_process';\n\nimport { createBoundedOutput, type IBoundedOutput } from '../../utils/bounded-output.js';\nimport { resolvePlatformShell } from '../../utils/platform-shell.js';\nimport { subprocessTraceEnvironment } from '../../utils/trace-context.js';\n\nimport type { ISubprocessTraceEnv } from '../../interfaces/trace-context.js';\nimport type {\n ICommandHookDefinition,\n IHookInput,\n THookOutcome,\n IHookTypeExecutor,\n} from '../types.js';\n\n/** Default timeout in seconds — matches Claude Code's 600s default */\nconst DEFAULT_TIMEOUT_SECONDS = 600;\n/** ARCH-056: most bytes retained per hook stream (stdout head, stderr tail). */\nconst MAX_HOOK_OUTPUT_BYTES = 1_000_000;\n\n/**\n * How a finished process maps onto an outcome — a pure function of what the process reported.\n *\n * Extracted from `execute` because it is the part that carries the contract: `execute` owns spawning\n * and settling exactly once, this owns what the result MEANS, and the two change for different\n * reasons.\n */\n/**\n * Attach the output collectors and feed the hook input to stdin.\n *\n * Separated from `execute` so the promise body reads as the three things that can settle it —\n * timeout, close, spawn error — rather than as stream plumbing interleaved with them. The\n * `settled` guard those three share is the invariant worth keeping visible in one place.\n */\nfunction wireChildIo(\n child: ReturnType<typeof spawn>,\n inputJson: string,\n): { stdoutOutput: IBoundedOutput; stderrOutput: IBoundedOutput } {\n // ARCH-056: bounded while reading; a hook that floods stdout cannot grow the parent's memory.\n const stdoutOutput = createBoundedOutput({ maxBytes: MAX_HOOK_OUTPUT_BYTES });\n const stderrOutput = createBoundedOutput({ maxBytes: MAX_HOOK_OUTPUT_BYTES, retain: 'tail' });\n child.stdout?.on('data', (chunk: Buffer) => stdoutOutput.append(chunk));\n child.stderr?.on('data', (chunk: Buffer) => stderrOutput.append(chunk));\n child.stdin?.on('error', () => {\n // EPIPE: child closed stdin before we finished writing — safe to ignore\n });\n child.stdin?.write(inputJson);\n child.stdin?.end();\n return { stdoutOutput, stderrOutput };\n}\n\nfunction outcomeOfExit(\n code: number | null,\n signal: NodeJS.Signals | null,\n stdout: string,\n stderr: string,\n): THookOutcome {\n if (code === 0) return { outcome: 'allow', source: 'command', stdout };\n if (code === 2) {\n return { outcome: 'deny', source: 'command', reason: stderr.trim() || 'Blocked by hook' };\n }\n const detail = stderr.trim() ? `: ${stderr.trim()}` : '';\n return {\n outcome: 'error',\n source: 'command',\n kind: 'nonzero-exit',\n // `code === null` means a signal ended it. That is not an exit code and must not be read as\n // one — the old `code ?? 1` made a SIGKILLed hook indistinguishable from one that exited 1.\n reason:\n code === null\n ? `Hook terminated by signal ${signal ?? 'unknown'}${detail}`\n : `Hook exited ${code}${detail}`,\n };\n}\n\nexport class CommandExecutor implements IHookTypeExecutor {\n readonly type = 'command' as const;\n\n constructor(private readonly shellExecutable?: string) {}\n\n /**\n * `traceEnv` is the prompt's trace for this child, handed separately so it never reaches the\n * stdin JSON. The child sees `process.env`, then ConversationAgent's value, then the hook's own `env`; a hook\n * that sets its own `TRACEPARENT` wins and gets the ambient environment unchanged.\n */\n execute(\n definition: ICommandHookDefinition,\n input: IHookInput,\n traceEnv?: ISubprocessTraceEnv,\n ): Promise<THookOutcome> {\n const timeoutSeconds = definition.timeout ?? DEFAULT_TIMEOUT_SECONDS;\n const timeoutMs = timeoutSeconds * 1000;\n const inputJson = JSON.stringify(input);\n\n return new Promise<THookOutcome>((resolve) => {\n let settled = false;\n const shell = resolvePlatformShell({ executable: this.shellExecutable });\n const child = spawn(shell.command, shell.commandArgs(definition.command), {\n cwd: input.cwd,\n env: subprocessTraceEnvironment(process.env, traceEnv, input.env),\n });\n const { stdoutOutput, stderrOutput } = wireChildIo(child, inputJson);\n\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true;\n child.kill('SIGTERM');\n resolve({\n outcome: 'error',\n source: 'command',\n kind: 'timeout',\n reason: `Hook timed out after ${timeoutSeconds}s`,\n });\n }\n }, timeoutMs);\n\n child.on('close', (code: number | null, signal: NodeJS.Signals | null) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(outcomeOfExit(code, signal, stdoutOutput.toString(), stderrOutput.toString()));\n });\n\n child.on('error', (err: Error) => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve({\n outcome: 'error',\n source: 'command',\n kind: 'spawn-failure',\n reason: err.message,\n });\n });\n });\n }\n}\n"],"mappings":"0GA+BA,MAEM,EAAwB,IAgB9B,SAAS,EACP,EACA,EACgE,CAEhE,IAAM,EAAe,EAAoB,CAAE,SAAU,CAAsB,CAAC,EACtE,EAAe,EAAoB,CAAE,SAAU,EAAuB,OAAQ,MAAO,CAAC,EAQ5F,OAPA,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,OAAO,CAAK,CAAC,EACtE,EAAM,QAAQ,GAAG,OAAS,GAAkB,EAAa,OAAO,CAAK,CAAC,EACtE,EAAM,OAAO,GAAG,YAAe,CAE/B,CAAC,EACD,EAAM,OAAO,MAAM,CAAS,EAC5B,EAAM,OAAO,IAAI,EACV,CAAE,eAAc,cAAa,CACtC,CAEA,SAAS,EACP,EACA,EACA,EACA,EACc,CACd,GAAI,IAAS,EAAG,MAAO,CAAE,QAAS,QAAS,OAAQ,UAAW,QAAO,EACrE,GAAI,IAAS,EACX,MAAO,CAAE,QAAS,OAAQ,OAAQ,UAAW,OAAQ,EAAO,KAAK,GAAK,iBAAkB,EAE1F,IAAM,EAAS,EAAO,KAAK,EAAI,KAAK,EAAO,KAAK,IAAM,GACtD,MAAO,CACL,QAAS,QACT,OAAQ,UACR,KAAM,eAGN,OACE,IAAS,KACL,6BAA6B,GAAU,YAAY,IACnD,eAAe,IAAO,GAC9B,CACF,CAEA,IAAa,EAAb,KAA0D,CAG3B,gBAF7B,KAAgB,UAEhB,YAAY,EAA2C,CAA1B,KAAA,gBAAA,CAA2B,CAOxD,QACE,EACA,EACA,EACuB,CACvB,IAAM,EAAiB,EAAW,SAAW,IACvC,EAAY,EAAiB,IAC7B,EAAY,KAAK,UAAU,CAAK,EAEtC,OAAO,IAAI,QAAuB,GAAY,CAC5C,IAAI,EAAU,GACR,EAAQ,EAAqB,CAAE,WAAY,KAAK,eAAgB,CAAC,EACjE,EAAQ,EAAM,EAAM,QAAS,EAAM,YAAY,EAAW,OAAO,EAAG,CACxE,IAAK,EAAM,IACX,IAAK,EAA2B,QAAQ,IAAK,EAAU,EAAM,GAAG,CAClE,CAAC,EACK,CAAE,eAAc,gBAAiB,EAAY,EAAO,CAAS,EAE7D,EAAQ,eAAiB,CACxB,IACH,EAAU,GACV,EAAM,KAAK,SAAS,EACpB,EAAQ,CACN,QAAS,QACT,OAAQ,UACR,KAAM,UACN,OAAQ,wBAAwB,EAAe,EACjD,CAAC,EAEL,EAAG,CAAS,EAEZ,EAAM,GAAG,SAAU,EAAqB,IAAkC,CACpE,IACJ,EAAU,GACV,aAAa,CAAK,EAClB,EAAQ,EAAc,EAAM,EAAQ,EAAa,SAAS,EAAG,EAAa,SAAS,CAAC,CAAC,EACvF,CAAC,EAED,EAAM,GAAG,QAAU,GAAe,CAC5B,IACJ,EAAU,GACV,aAAa,CAAK,EAClB,EAAQ,CACN,QAAS,QACT,OAAQ,UACR,KAAM,gBACN,OAAQ,EAAI,OACd,CAAC,EACH,CAAC,CACH,CAAC,CACH,CACF"}
|