@sema-agent/sdk 0.0.75 → 0.0.77
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/dist/client.d.ts +48 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +55 -2
- package/dist/client.js.map +1 -1
- package/dist/control-client.d.ts +21 -0
- package/dist/control-client.d.ts.map +1 -1
- package/dist/control-client.js +42 -1
- package/dist/control-client.js.map +1 -1
- package/dist/control-types.d.ts +108 -0
- package/dist/control-types.d.ts.map +1 -1
- package/dist/control-types.js +15 -0
- package/dist/control-types.js.map +1 -1
- package/dist/errors.d.ts +88 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +105 -8
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +214 -20
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +5 -0
- package/dist/events.js.map +1 -1
- package/dist/health.d.ts +44 -0
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +32 -0
- package/dist/health.js.map +1 -1
- package/dist/idempotency.d.ts +8 -0
- package/dist/idempotency.d.ts.map +1 -1
- package/dist/idempotency.js +8 -0
- package/dist/idempotency.js.map +1 -1
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/approvals.d.ts +43 -0
- package/dist/resources/approvals.d.ts.map +1 -1
- package/dist/resources/approvals.js +23 -3
- package/dist/resources/approvals.js.map +1 -1
- package/dist/resources/assistant.d.ts +23 -0
- package/dist/resources/assistant.d.ts.map +1 -1
- package/dist/resources/assistant.js +22 -0
- package/dist/resources/assistant.js.map +1 -1
- package/dist/resources/control/auth-providers.d.ts +17 -0
- package/dist/resources/control/auth-providers.d.ts.map +1 -1
- package/dist/resources/control/auth-providers.js +6 -0
- package/dist/resources/control/auth-providers.js.map +1 -1
- package/dist/resources/control/config.d.ts +22 -0
- package/dist/resources/control/config.d.ts.map +1 -1
- package/dist/resources/control/config.js +12 -0
- package/dist/resources/control/config.js.map +1 -1
- package/dist/resources/control/fleet.d.ts +15 -0
- package/dist/resources/control/fleet.d.ts.map +1 -1
- package/dist/resources/control/fleet.js +8 -0
- package/dist/resources/control/fleet.js.map +1 -1
- package/dist/resources/control/images.d.ts +20 -0
- package/dist/resources/control/images.d.ts.map +1 -1
- package/dist/resources/control/images.js +10 -0
- package/dist/resources/control/images.js.map +1 -1
- package/dist/resources/control/lifecycle.d.ts +8 -0
- package/dist/resources/control/lifecycle.d.ts.map +1 -1
- package/dist/resources/control/lifecycle.js +3 -0
- package/dist/resources/control/lifecycle.js.map +1 -1
- package/dist/resources/control/publish.d.ts +12 -0
- package/dist/resources/control/publish.d.ts.map +1 -1
- package/dist/resources/control/publish.js +6 -0
- package/dist/resources/control/publish.js.map +1 -1
- package/dist/resources/control/secrets.d.ts +28 -0
- package/dist/resources/control/secrets.d.ts.map +1 -1
- package/dist/resources/control/secrets.js +16 -0
- package/dist/resources/control/secrets.js.map +1 -1
- package/dist/resources/control/users.d.ts +22 -0
- package/dist/resources/control/users.d.ts.map +1 -1
- package/dist/resources/control/users.js +12 -0
- package/dist/resources/control/users.js.map +1 -1
- package/dist/resources/control/versioning.d.ts +12 -0
- package/dist/resources/control/versioning.d.ts.map +1 -1
- package/dist/resources/control/versioning.js +6 -0
- package/dist/resources/control/versioning.js.map +1 -1
- package/dist/resources/control/workers.d.ts +14 -0
- package/dist/resources/control/workers.d.ts.map +1 -1
- package/dist/resources/control/workers.js +7 -0
- package/dist/resources/control/workers.js.map +1 -1
- package/dist/resources/elicitations.d.ts +43 -0
- package/dist/resources/elicitations.d.ts.map +1 -1
- package/dist/resources/elicitations.js +8 -0
- package/dist/resources/elicitations.js.map +1 -1
- package/dist/resources/fleet.d.ts +101 -0
- package/dist/resources/fleet.d.ts.map +1 -1
- package/dist/resources/fleet.js +27 -4
- package/dist/resources/fleet.js.map +1 -1
- package/dist/resources/images.d.ts +34 -0
- package/dist/resources/images.d.ts.map +1 -1
- package/dist/resources/images.js +29 -3
- package/dist/resources/images.js.map +1 -1
- package/dist/resources/leader.d.ts +10 -0
- package/dist/resources/leader.d.ts.map +1 -1
- package/dist/resources/leader.js +4 -0
- package/dist/resources/leader.js.map +1 -1
- package/dist/resources/memory.d.ts +37 -0
- package/dist/resources/memory.d.ts.map +1 -1
- package/dist/resources/memory.js +28 -0
- package/dist/resources/memory.js.map +1 -1
- package/dist/resources/models.d.ts +12 -0
- package/dist/resources/models.d.ts.map +1 -1
- package/dist/resources/models.js +4 -0
- package/dist/resources/models.js.map +1 -1
- package/dist/resources/ops.d.ts +16 -0
- package/dist/resources/ops.d.ts.map +1 -1
- package/dist/resources/ops.js +6 -0
- package/dist/resources/ops.js.map +1 -1
- package/dist/resources/policy.d.ts +8 -0
- package/dist/resources/policy.d.ts.map +1 -1
- package/dist/resources/policy.js +1 -0
- package/dist/resources/policy.js.map +1 -1
- package/dist/resources/questions.d.ts +57 -0
- package/dist/resources/questions.d.ts.map +1 -1
- package/dist/resources/questions.js +9 -0
- package/dist/resources/questions.js.map +1 -1
- package/dist/resources/runs.d.ts +147 -0
- package/dist/resources/runs.d.ts.map +1 -1
- package/dist/resources/runs.js +129 -2
- package/dist/resources/runs.js.map +1 -1
- package/dist/resources/session-sync.d.ts +61 -0
- package/dist/resources/session-sync.d.ts.map +1 -1
- package/dist/resources/session-sync.js +54 -1
- package/dist/resources/session-sync.js.map +1 -1
- package/dist/resources/sessions.d.ts +74 -0
- package/dist/resources/sessions.d.ts.map +1 -1
- package/dist/resources/sessions.js +64 -2
- package/dist/resources/sessions.js.map +1 -1
- package/dist/resources/side-query.d.ts +18 -0
- package/dist/resources/side-query.d.ts.map +1 -1
- package/dist/resources/side-query.js +1 -0
- package/dist/resources/side-query.js.map +1 -1
- package/dist/resources/tasks.d.ts +19 -0
- package/dist/resources/tasks.d.ts.map +1 -1
- package/dist/resources/tasks.js +21 -2
- package/dist/resources/tasks.js.map +1 -1
- package/dist/resources/tool-approvals.d.ts +60 -0
- package/dist/resources/tool-approvals.d.ts.map +1 -1
- package/dist/resources/tool-approvals.js +10 -0
- package/dist/resources/tool-approvals.js.map +1 -1
- package/dist/resources/trace.d.ts +23 -0
- package/dist/resources/trace.d.ts.map +1 -1
- package/dist/resources/trace.js +27 -2
- package/dist/resources/trace.js.map +1 -1
- package/dist/resources/usage.d.ts +21 -0
- package/dist/resources/usage.d.ts.map +1 -1
- package/dist/resources/usage.js +8 -0
- package/dist/resources/usage.js.map +1 -1
- package/dist/resources/workflows.d.ts +48 -0
- package/dist/resources/workflows.d.ts.map +1 -1
- package/dist/resources/workflows.js +40 -3
- package/dist/resources/workflows.js.map +1 -1
- package/dist/settings.d.ts +90 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +24 -0
- package/dist/settings.js.map +1 -1
- package/dist/sse.d.ts +36 -0
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +53 -8
- package/dist/sse.js.map +1 -1
- package/dist/sync.d.ts +56 -0
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +48 -4
- package/dist/sync.js.map +1 -1
- package/dist/transport.d.ts +32 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +40 -8
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +709 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/settings.d.ts
CHANGED
|
@@ -1,44 +1,134 @@
|
|
|
1
|
+
/** TOC `settings.json` — the SHARED environment-customization contract (CC-parity v1 subset).
|
|
2
|
+
*
|
|
3
|
+
* Owned by @sema-agent/sdk **on purpose** (2026-06-27, research/toc-settings-adapter/01-design.md §5①):
|
|
4
|
+
* the same user `settings.json` must drive BOTH wirings off ONE schema —
|
|
5
|
+
* - **TOC-local**: the CLI `run --local` / the shell host read it → wire into a local `new Runner(deps)` (the shell host's
|
|
6
|
+
* adapter + shell-hook runner). The SDK does NOT run shell or interpret settings — it only OWNS the type.
|
|
7
|
+
* - **TOB-fleet**: the SDK carries it to the service ({@link TaskRequest.settings}); the **service** projects it into
|
|
8
|
+
* the SAME core seam (SessionPolicyStore / NodeExecutionEnv / RunnerDeps.hooks) — its layer, same as the MF-* wiring.
|
|
9
|
+
*
|
|
10
|
+
* RED LINE (primitive/profile): this file is the CONTRACT only. No settings-interpreter logic, no shell-hook runner
|
|
11
|
+
* lives in the SDK (that is the TOC profile/shell layer's job — 01-design.md §3.2/§5②).
|
|
12
|
+
*
|
|
13
|
+
* ## Mapping — `SemaSettings` key → core engine seam (the adapter wires this; cited for alignment, not re-exported)
|
|
14
|
+
* | settings key | core seam (落点) | source |
|
|
15
|
+
* |-------------------------|--------------------------------------------------------------------|-----------------------------------------|
|
|
16
|
+
* | `permissions` | `SessionPolicyStore` → {@link SessionPermissionRules} (tighten-only)| core session-policy-store.ts:26 |
|
|
17
|
+
* | `permissions.defaultMode` (permissionMode) | adapter **derive** → `toolPolicy`/`onAsk`/`handsReadOnly`(default/acceptEdits/plan;bypass=launch-flag only) | core v2-design §6.4 |
|
|
18
|
+
* | `hooks` | `RunnerDeps.hooks` = core `Hooks{preToolUse/postToolUse/userPromptSubmit}` (the shell-hook runner maps exit-code→`PreToolUseResult`) | core hooks.ts:22 / hooks.d.ts:19 |
|
|
19
|
+
* | `env` | `NodeExecutionEnv({ shellEnv, inheritEnv })` (secret-scrub default) | core harness/env/nodejs.ts |
|
|
20
|
+
* | `model` / `outputStyle` | `TaskSpec.model` / `RoleSpec.systemPrompt` / `appendSystemPrompt` | core types.ts |
|
|
21
|
+
* Security invariants the adapter must preserve (01-design.md §3.3): hook `allow` NEVER bypasses `toolPolicy` deny/ask
|
|
22
|
+
* (core two-phase gate); `permissions` is tighten-only (loosening needs an operator principal); env + hook stdin/stdout
|
|
23
|
+
* are secret-scrubbed; `additionalDirectories` is posix-normalized before use.
|
|
24
|
+
*/
|
|
25
|
+
/** A single hook command. v1 = CC "command" form only (CC's `prompt`/`agent` LLM-hook forms are DEFERRED). */
|
|
1
26
|
export interface SettingsHookCommand {
|
|
27
|
+
/** v1 supports the exec-form only. */
|
|
2
28
|
type: "command";
|
|
29
|
+
/** The shell command/script to run. The TOC shell-hook runner spawns it (stdin = the hook event JSON, CC protocol)
|
|
30
|
+
* and maps its exit code → a typed core hook result (PreToolUse: 0=allow, 2=deny/block, other=non-blocking error). */
|
|
3
31
|
command: string;
|
|
32
|
+
/** Per-command timeout in SECONDS (CC semantics). Omitted = the runner's default. */
|
|
4
33
|
timeout?: number;
|
|
34
|
+
/** Open: tolerate CC command-form keys deferred in v1 (`args`/`shell`/`once`/`async`/`statusMessage`/`if`/…). */
|
|
5
35
|
[k: string]: unknown;
|
|
6
36
|
}
|
|
37
|
+
/** One matcher entry: which tools (by name pattern) run which hook commands. */
|
|
7
38
|
export interface SettingsHookMatcher {
|
|
39
|
+
/** Tool-name pattern (e.g. `"Bash"`, `"Bash|Edit"`, `"Write"`). Omitted/empty ⇒ matches every tool.
|
|
40
|
+
* UserPromptSubmit hooks have no tool → matcher is ignored there. */
|
|
8
41
|
matcher?: string;
|
|
42
|
+
/** The hook commands to run when `matcher` hits (the runner handles dedup/parallel-precedence/timeout). */
|
|
9
43
|
hooks: SettingsHookCommand[];
|
|
10
44
|
}
|
|
45
|
+
/** CC-parity hook EVENTS, v1 = the 3 actionable tool/prompt events (01-design.md §4).
|
|
46
|
+
* DEFERRED: lifecycle events (SessionStart/SessionEnd/Stop/PreCompact/SubagentStop) — the TOC client owns launch, so
|
|
47
|
+
* env-setup happens at launch; a `onTaskStart/End` core seam is demand-driven only. PostToolUseFailure also deferred. */
|
|
11
48
|
export interface SettingsHooks {
|
|
49
|
+
/** Before a tool runs. Maps to core `Hooks.preToolUse` → `PreToolUseResult` (deny/ask, or allow+`updatedInput`,
|
|
50
|
+
* + `additionalContext`). 🔴 a hook `allow` can NEVER override a `toolPolicy` deny/ask (core two-phase gate). */
|
|
12
51
|
PreToolUse?: SettingsHookMatcher[];
|
|
52
|
+
/** After a tool succeeds. Maps to core `Hooks.postToolUse` → `PostToolUseResult` (replace `updatedOutput` / append context). */
|
|
13
53
|
PostToolUse?: SettingsHookMatcher[];
|
|
54
|
+
/** When the user submits a prompt. Maps to core `Hooks.userPromptSubmit` → `UserPromptSubmitResult` (block / inject context). */
|
|
14
55
|
UserPromptSubmit?: SettingsHookMatcher[];
|
|
56
|
+
/** Open: tolerate CC hook events deferred in v1. */
|
|
15
57
|
[event: string]: SettingsHookMatcher[] | undefined;
|
|
16
58
|
}
|
|
59
|
+
/** CC-parity permission rules — string-PATTERN arrays (e.g. `"Bash(git *)"`, `"Read"`, `"Bash(rm -rf *)"`).
|
|
60
|
+
* Maps to core `SessionPolicyStore` → {@link SessionPermissionRules} (commandAllow/Deny by argv[0], toolAllow/Deny,
|
|
61
|
+
* allowDirs). 🔴 tighten-only: a normal (non-operator) write may only narrow; loosening → 403 `loosen_forbidden`. */
|
|
17
62
|
export interface SettingsPermissions {
|
|
63
|
+
/** Allow-list patterns (auto-approve). */
|
|
18
64
|
allow?: string[];
|
|
65
|
+
/** Deny-list patterns (hard block; deny short-circuits). */
|
|
19
66
|
deny?: string[];
|
|
67
|
+
/** Ask-list patterns (prompt the human). */
|
|
20
68
|
ask?: string[];
|
|
69
|
+
/** Extra directories added to the permission/fs scope (= CC `--add-dir`). posix-normalized before use. */
|
|
21
70
|
additionalDirectories?: string[];
|
|
71
|
+
/** 🔴 CC-parity permission MODE (2026-06-27 un-defer; core v2-design §6.4). The adapter DERIVES this onto the
|
|
72
|
+
* core substrate (no engine `permissionMode` field): `default`=gate每次问人(toolPolicy+onAsk ask-list)·
|
|
73
|
+
* `acceptEdits`=自动批准 Edit/Write、其余仍 gate(toolPolicy 合成)· `plan`=只读规划(`handsReadOnly:true`+shell plan-审批 UX)。
|
|
74
|
+
* 🔴 **`bypassPermissions`(=`--dangerously-skip-permissions`)NOT here** —— 只经 launch-flag 进入、绝不持久进 settings
|
|
75
|
+
* 文件(YOLO 不进可提交文件,同 CC);失败一律回退 `default`,绝不回退 bypass。 */
|
|
22
76
|
defaultMode?: "default" | "acceptEdits" | "plan";
|
|
77
|
+
/** MANAGED kill-switch (CC enterprise key): MANAGED 层 enforce 后即便给 launch-flag 也拒 bypass(§6.4 admin ceiling)。 */
|
|
23
78
|
disableBypassPermissionsMode?: boolean;
|
|
79
|
+
/** MANAGED kill-switch: 禁 `acceptEdits` 自动批准模式(同 disableBypassPermissionsMode 机制)。 */
|
|
24
80
|
disableAutoMode?: boolean;
|
|
81
|
+
/** Open: tolerate further CC permission keys deferred in v1. */
|
|
25
82
|
[k: string]: unknown;
|
|
26
83
|
}
|
|
84
|
+
/** Env-var injection map → core `NodeExecutionEnv({ shellEnv })`. Values are NEVER logged (core inheritEnv scrub). */
|
|
27
85
|
export type SettingsEnv = Record<string, string>;
|
|
86
|
+
/** Per-request WebSearch BACKEND config → service `body.settings.webSearch`. The service
|
|
87
|
+
* (the service `src/plugins/web-search.ts` `webSearchConfigFromSettings`) parses `{ provider, apiKey?,
|
|
88
|
+
* endpoint?, maxResults? }`: a per-request config that WINS over the deployment-env (`WEB_SEARCH_*`) backend,
|
|
89
|
+
* 🔒 gated to the SINGLE-USER host lane (a per-request `endpoint`/`apiKey` is a capability config; on
|
|
90
|
+
* multi-tenant a tenant could point `searxng` at an internal URL = SSRF, so multi-tenant honors ONLY the
|
|
91
|
+
* deploy-env backend — design/107). A missing/invalid `provider` is DROPPED service-side (falls back to the
|
|
92
|
+
* deploy-env backend) — never an error. Wiring a backend is what makes the engine actually mount WebSearch. */
|
|
28
93
|
export interface SettingsWebSearch {
|
|
94
|
+
/** Search provider. Only `brave` | `tavily` | `searxng` are honored; anything else ⇒ dropped (deploy-env fallback). */
|
|
29
95
|
provider?: "brave" | "tavily" | "searxng";
|
|
96
|
+
/** API key (brave/tavily). Held in a service-side closure — it never reaches the model prompt or the tool args. */
|
|
30
97
|
apiKey?: string;
|
|
98
|
+
/** SearXNG instance base URL (REQUIRED for searxng); for brave/tavily an optional base-URL override (proxy/test). */
|
|
31
99
|
endpoint?: string;
|
|
100
|
+
/** Max results returned to the model (the service clamps 1..20; default 10). */
|
|
32
101
|
maxResults?: number;
|
|
33
102
|
}
|
|
103
|
+
/** The TOC `settings.json` SHARED contract (CC-parity v1 subset). One schema, two wirings (TOC-local / TOB-fleet).
|
|
104
|
+
* CC-parity anchor: claude-code-2.1.187 settings-hooks.md. Open at the top level to tolerate deferred CC keys
|
|
105
|
+
* (`statusLine` — shell UI, shell-host self-managed, never touches the engine; `apiKeyHelper`; `sandbox` advanced). */
|
|
34
106
|
export interface SemaSettings {
|
|
107
|
+
/** Permission allow/deny/ask + additionalDirectories → SessionPolicyStore (tighten-only). */
|
|
35
108
|
permissions?: SettingsPermissions;
|
|
109
|
+
/** Shell hooks (PreToolUse/PostToolUse/UserPromptSubmit) → RunnerDeps.hooks (the shell-hook runner runs them). */
|
|
36
110
|
hooks?: SettingsHooks;
|
|
111
|
+
/** Env-var injection → NodeExecutionEnv({ shellEnv }). */
|
|
37
112
|
env?: SettingsEnv;
|
|
113
|
+
/** Model id → TaskSpec.model. */
|
|
38
114
|
model?: string;
|
|
115
|
+
/** Output style → appendSystemPrompt / RoleSpec.systemPrompt. */
|
|
39
116
|
outputStyle?: string;
|
|
117
|
+
/** WebSearch backend config → service `body.settings.webSearch` (per-request WINS over deploy env; single-user
|
|
118
|
+
* gate). Lets the TOC shell pick the WebSearch provider/endpoint per request (e.g. a self-hosted SearXNG). */
|
|
40
119
|
webSearch?: SettingsWebSearch;
|
|
120
|
+
/** 🔴 CC-parity `settings.ultracode` PRESET (claude-code-2.1.187; design/111 L2, core/search AI 2026-06-30).
|
|
121
|
+
* CC models ultracode as a STANDALONE settings boolean (NOT an effort enum) — a bundled preset folding two
|
|
122
|
+
* orthogonal axes: `max thinking` × `workflow orchestration`. The SERVICE (1.48.0, `e08d0c2`)
|
|
123
|
+
* reads `body.settings.ultracode` and EXPANDS it to `thinking:"max"` (via core
|
|
124
|
+
* `resolveReasoningProfile("ultra").thinking`, single-source — never hardcoded) + `selfOrchestration:true`
|
|
125
|
+
* (OR'd into `selfOrchestrationFromBody`, the SAME multi-tenant gate — no bypass). Both underlying axes are
|
|
126
|
+
* core's existing orthogonal fields; this is a pure settings-expansion, zero new mechanism. Privilege still
|
|
127
|
+
* passes the service's fail-closed `allowWorkflows` gate (the preset is a UX switch, not a security decision).
|
|
128
|
+
* The TOC shell stamps this from the `/effort ultracode` dial pick OR a client-side "ultracode" keyword scan of
|
|
129
|
+
* the REAL human turn input (design/111 L3 — NEVER the assembled prompt: an injection boundary). */
|
|
41
130
|
ultracode?: boolean;
|
|
131
|
+
/** Open: tolerate deferred CC top-level keys (statusLine/apiKeyHelper/sandbox/…) without a parse break. */
|
|
42
132
|
[k: string]: unknown;
|
|
43
133
|
}
|
|
44
134
|
//# sourceMappingURL=settings.d.ts.map
|
package/dist/settings.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,8GAA8G;AAC9G,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,IAAI,EAAE,SAAS,CAAC;IAChB;2HACuH;IACvH,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iHAAiH;IACjH,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,gFAAgF;AAChF,MAAM,WAAW,mBAAmB;IAClC;0EACsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2GAA2G;IAC3G,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B;AAED;;0HAE0H;AAC1H,MAAM,WAAW,aAAa;IAC5B;sHACkH;IAClH,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACnC,gIAAgI;IAChI,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACpC,iIAAiI;IACjI,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACzC,oDAAoD;IACpD,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,EAAE,GAAG,SAAS,CAAC;CACpD;AAED;;sHAEsH;AACtH,MAAM,WAAW,mBAAmB;IAClC,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,0GAA0G;IAC1G,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC;;;;8DAI0D;IAC1D,WAAW,CAAC,EAAE,SAAS,GAAG,aAAa,GAAG,MAAM,CAAC;IACjD,iHAAiH;IACjH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,sFAAsF;IACtF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gEAAgE;IAChE,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED,sHAAsH;AACtH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD;;;;;;gHAMgH;AAChH,MAAM,WAAW,iBAAiB;IAChC,uHAAuH;IACvH,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC1C,mHAAmH;IACnH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qHAAqH;IACrH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;wHAEwH;AACxH,MAAM,WAAW,YAAY;IAC3B,6FAA6F;IAC7F,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,kHAAkH;IAClH,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,0DAA0D;IAC1D,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;mHAC+G;IAC/G,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B;;;;;;;;;yGASqG;IACrG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2GAA2G;IAC3G,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB"}
|
package/dist/settings.js
CHANGED
|
@@ -1,2 +1,26 @@
|
|
|
1
|
+
/** TOC `settings.json` — the SHARED environment-customization contract (CC-parity v1 subset).
|
|
2
|
+
*
|
|
3
|
+
* Owned by @sema-agent/sdk **on purpose** (2026-06-27, research/toc-settings-adapter/01-design.md §5①):
|
|
4
|
+
* the same user `settings.json` must drive BOTH wirings off ONE schema —
|
|
5
|
+
* - **TOC-local**: the CLI `run --local` / the shell host read it → wire into a local `new Runner(deps)` (the shell host's
|
|
6
|
+
* adapter + shell-hook runner). The SDK does NOT run shell or interpret settings — it only OWNS the type.
|
|
7
|
+
* - **TOB-fleet**: the SDK carries it to the service ({@link TaskRequest.settings}); the **service** projects it into
|
|
8
|
+
* the SAME core seam (SessionPolicyStore / NodeExecutionEnv / RunnerDeps.hooks) — its layer, same as the MF-* wiring.
|
|
9
|
+
*
|
|
10
|
+
* RED LINE (primitive/profile): this file is the CONTRACT only. No settings-interpreter logic, no shell-hook runner
|
|
11
|
+
* lives in the SDK (that is the TOC profile/shell layer's job — 01-design.md §3.2/§5②).
|
|
12
|
+
*
|
|
13
|
+
* ## Mapping — `SemaSettings` key → core engine seam (the adapter wires this; cited for alignment, not re-exported)
|
|
14
|
+
* | settings key | core seam (落点) | source |
|
|
15
|
+
* |-------------------------|--------------------------------------------------------------------|-----------------------------------------|
|
|
16
|
+
* | `permissions` | `SessionPolicyStore` → {@link SessionPermissionRules} (tighten-only)| core session-policy-store.ts:26 |
|
|
17
|
+
* | `permissions.defaultMode` (permissionMode) | adapter **derive** → `toolPolicy`/`onAsk`/`handsReadOnly`(default/acceptEdits/plan;bypass=launch-flag only) | core v2-design §6.4 |
|
|
18
|
+
* | `hooks` | `RunnerDeps.hooks` = core `Hooks{preToolUse/postToolUse/userPromptSubmit}` (the shell-hook runner maps exit-code→`PreToolUseResult`) | core hooks.ts:22 / hooks.d.ts:19 |
|
|
19
|
+
* | `env` | `NodeExecutionEnv({ shellEnv, inheritEnv })` (secret-scrub default) | core harness/env/nodejs.ts |
|
|
20
|
+
* | `model` / `outputStyle` | `TaskSpec.model` / `RoleSpec.systemPrompt` / `appendSystemPrompt` | core types.ts |
|
|
21
|
+
* Security invariants the adapter must preserve (01-design.md §3.3): hook `allow` NEVER bypasses `toolPolicy` deny/ask
|
|
22
|
+
* (core two-phase gate); `permissions` is tighten-only (loosening needs an operator principal); env + hook stdin/stdout
|
|
23
|
+
* are secret-scrubbed; `additionalDirectories` is posix-normalized before use.
|
|
24
|
+
*/
|
|
1
25
|
export {};
|
|
2
26
|
//# sourceMappingURL=settings.js.map
|
package/dist/settings.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG"}
|
package/dist/sse.d.ts
CHANGED
|
@@ -1,16 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE parser (fetch ReadableStream, Node/browser同源) + Last-Event-ID resume loop, normalizing both stream
|
|
3
|
+
* sources into the AgentEvent union (events.ts). On a server-window eviction the server returns 416 with
|
|
4
|
+
* `{ error, retainedFrom }` (match the STATUS — there is NO RESUME_EVICTED code field) → full-sync via the
|
|
5
|
+
* trace turns API (GET /v1/tasks/:id/turns) then resume events from `retainedFrom` (no event lost, only
|
|
6
|
+
* possibly re-seen; consumer is event-id idempotent). See the pinned SSE re-delivery contract.
|
|
7
|
+
* Heartbeat-timeout (service heartbeat = 30s; SDK threshold ≥ ~45s) → treat as a drop, enter the resume loop.
|
|
8
|
+
*
|
|
9
|
+
* STATUS: M2 — implemented. `open(lastEventId?)` is the connection factory (the caller binds path/transport);
|
|
10
|
+
* the resume loop feeds back the last seen event id. Terminal events (done/failed) end the generator.
|
|
11
|
+
*/
|
|
1
12
|
import type { AgentEvent } from "./events.js";
|
|
2
13
|
export declare const HEARTBEAT_GRACE_MS = 45000;
|
|
14
|
+
/** Heartbeat-silence watchdog fired: no bytes arrived within the grace window. Typed so a caller (shell
|
|
15
|
+
* liveClient tear-verdict) can DIAGNOSE "engine alive but stream idle" apart from other stream errors.
|
|
16
|
+
* 🔴 The message is a FROZEN literal (`SSE: no data for ${ms}ms (heartbeat silence)`) — downstream scripts
|
|
17
|
+
* grep stderr for it; change the type surface, never the words. (P0-2 S1, TB2.0 triage 2026-07-15.) */
|
|
3
18
|
export declare class SseIdleError extends Error {
|
|
4
19
|
readonly elapsedMs: number;
|
|
5
20
|
constructor(elapsedMs: number);
|
|
6
21
|
}
|
|
22
|
+
/** Resumable stream: (re)opens via `open(lastEventId)`, parses SSE frames into AgentEvents, resumes on drops
|
|
23
|
+
* and heartbeat silences, handles 416 eviction by resuming from `retainedFrom`. Ends on done/failed. */
|
|
7
24
|
export declare function parseSse(open: (lastEventId?: string) => Promise<Response>, opts?: {
|
|
8
25
|
heartbeatGraceMs?: number;
|
|
9
26
|
signal?: AbortSignal;
|
|
10
27
|
}): AsyncGenerator<AgentEvent>;
|
|
28
|
+
/** A yielded event carries the durable SSE id (task_event.seq) when present — an SSE relay (web BFF)
|
|
29
|
+
* forwards it so the browser's EventSource can resume with Last-Event-ID end-to-end. */
|
|
11
30
|
export type StampedAgentEvent = AgentEvent & {
|
|
12
31
|
id?: string;
|
|
13
32
|
};
|
|
33
|
+
/** Generic RESUMABLE frame reader — the vocabulary-agnostic sibling of `parseSse`. Unlike `parseSse` (which is
|
|
34
|
+
* hardcoded to the AgentEvent union + its terminal `done`/`failed`), this yields RAW `SseFrame`s so a caller with
|
|
35
|
+
* a DIFFERENT SSE vocabulary (the trace relay's `block-*`/`turn`/`tool-*` frames, mapTraceEvent) can resume on
|
|
36
|
+
* drops and on a 416 eviction WITHOUT inheriting AgentEvent semantics. Reconnect is driven by the last seen
|
|
37
|
+
* `id:` (Last-Event-ID); a terminal frame is the CALLER's concern (it decides what `event` ends the stream — so
|
|
38
|
+
* pass `isTerminal`). Heartbeat/comment frames (no `data`) are yielded too (the caller filters). 416 → read
|
|
39
|
+
* `retainedFrom` and resume from there (the consumer full-syncs missed ground truth via the turns API). */
|
|
14
40
|
export declare function parseFrameStream(open: (lastEventId?: string) => Promise<Response>, isTerminal: (frame: SseFrame) => boolean, opts?: {
|
|
15
41
|
heartbeatGraceMs?: number;
|
|
16
42
|
signal?: AbortSignal;
|
|
@@ -20,11 +46,21 @@ export interface SseFrame {
|
|
|
20
46
|
event?: string;
|
|
21
47
|
data?: string;
|
|
22
48
|
}
|
|
49
|
+
/** Single-connection SSE frame reader (WHATWG format) with a heartbeat-silence watchdog. Throws on silence
|
|
50
|
+
* (caller decides resume vs abort). Used directly by tasks.stream (transient: a drop is NOT resumed). */
|
|
23
51
|
export declare function readSseFrames(res: Response, heartbeatGraceMs?: number, signal?: AbortSignal): AsyncGenerator<SseFrame>;
|
|
52
|
+
/** A `/sync/entries` NDJSON stream ended without its `{"__sync":"end",count}` trailer, or the trailer's count did
|
|
53
|
+
* not match the entries received — the stream was TRUNCATED (a mid-stream server failure after the 200). Client
|
|
54
|
+
* action: re-GET (optionally with `afterSeq` to resume from the last durable seq seen, same-backend only). */
|
|
24
55
|
export declare class SyncTruncatedError extends Error {
|
|
25
56
|
readonly received: number;
|
|
26
57
|
readonly expected?: number | undefined;
|
|
27
58
|
constructor(message: string, received: number, expected?: number | undefined);
|
|
28
59
|
}
|
|
60
|
+
/** Parse a 2c session-sync NDJSON entry stream from a (200) Response into the entry objects, validating the
|
|
61
|
+
* begin/end sentinel framing. Yields each entry line (the `__sync` header/trailer lines are consumed, NOT yielded).
|
|
62
|
+
* Throws {@link SyncTruncatedError} on a missing trailer or a count mismatch; rethrows a malformed-JSON line as a
|
|
63
|
+
* contract breach. The caller owns the Response (already status-checked: a non-200 is a JSON error body, handled
|
|
64
|
+
* before this is called). `T` defaults to `unknown` — the cloud re-validates the tree, so the SDK keeps entries opaque. */
|
|
29
65
|
export declare function parseNdjsonEntries<T = unknown>(res: Response, signal?: AbortSignal): AsyncGenerator<T>;
|
|
30
66
|
//# sourceMappingURL=sse.d.ts.map
|
package/dist/sse.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,eAAO,MAAM,kBAAkB,QAAS,CAAC;AAGzC;;;wGAGwG;AACxG,qBAAa,YAAa,SAAQ,KAAK;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM;gBAAjB,SAAS,EAAE,MAAM;CAIvC;AAED;yGACyG;AACzG,wBAAuB,QAAQ,CAC7B,IAAI,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,EACjD,IAAI,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACzD,cAAc,CAAC,UAAU,CAAC,CAsD5B;AAED;yFACyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG;IAAE,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D;;;;;;4GAM4G;AAC5G,wBAAuB,gBAAgB,CACrC,IAAI,EAAE,CAAC,WAAW,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,EACjD,UAAU,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,EACxC,IAAI,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACzD,cAAc,CAAC,QAAQ,CAAC,CA4C1B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;0GAC0G;AAC1G,wBAAuB,aAAa,CAClC,GAAG,EAAE,QAAQ,EACb,gBAAgB,SAAqB,EACrC,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,QAAQ,CAAC,CA4C1B;AAcD;;+GAE+G;AAC/G,qBAAa,kBAAmB,SAAQ,KAAK;IACd,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM;gBAAtE,OAAO,EAAE,MAAM,EAAW,QAAQ,EAAE,MAAM,EAAW,QAAQ,CAAC,EAAE,MAAM,YAAA;CAInF;AAED;;;;4HAI4H;AAC5H,wBAAuB,kBAAkB,CAAC,CAAC,GAAG,OAAO,EACnD,GAAG,EAAE,QAAQ,EACb,MAAM,CAAC,EAAE,WAAW,GACnB,cAAc,CAAC,CAAC,CAAC,CAuDnB"}
|
package/dist/sse.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { toApiError } from "./errors.js";
|
|
2
|
-
export const HEARTBEAT_GRACE_MS = 45_000;
|
|
2
|
+
export const HEARTBEAT_GRACE_MS = 45_000; // service heartbeat is 30s; allow one beat of slack before declaring a drop
|
|
3
3
|
const MAX_CONSECUTIVE_FAILURES = 5;
|
|
4
|
+
/** Heartbeat-silence watchdog fired: no bytes arrived within the grace window. Typed so a caller (shell
|
|
5
|
+
* liveClient tear-verdict) can DIAGNOSE "engine alive but stream idle" apart from other stream errors.
|
|
6
|
+
* 🔴 The message is a FROZEN literal (`SSE: no data for ${ms}ms (heartbeat silence)`) — downstream scripts
|
|
7
|
+
* grep stderr for it; change the type surface, never the words. (P0-2 S1, TB2.0 triage 2026-07-15.) */
|
|
4
8
|
export class SseIdleError extends Error {
|
|
5
9
|
elapsedMs;
|
|
6
10
|
constructor(elapsedMs) {
|
|
@@ -9,6 +13,8 @@ export class SseIdleError extends Error {
|
|
|
9
13
|
this.name = "SseIdleError";
|
|
10
14
|
}
|
|
11
15
|
}
|
|
16
|
+
/** Resumable stream: (re)opens via `open(lastEventId)`, parses SSE frames into AgentEvents, resumes on drops
|
|
17
|
+
* and heartbeat silences, handles 416 eviction by resuming from `retainedFrom`. Ends on done/failed. */
|
|
12
18
|
export async function* parseSse(open, opts) {
|
|
13
19
|
const grace = opts?.heartbeatGraceMs ?? HEARTBEAT_GRACE_MS;
|
|
14
20
|
let lastId;
|
|
@@ -27,7 +33,11 @@ export async function* parseSse(open, opts) {
|
|
|
27
33
|
continue;
|
|
28
34
|
}
|
|
29
35
|
if (res.status === 416) {
|
|
36
|
+
// evicted past retention: match the STATUS (no code field), read retainedFrom, resume from there.
|
|
37
|
+
// The consumer full-syncs missed ground truth via trace.turns (pinned SSE contract).
|
|
30
38
|
const body = (await res.json().catch(() => ({})));
|
|
39
|
+
// Last-Event-ID = "replay AFTER this seq" → ask from retainedFrom-1 so retainedFrom itself is replayed
|
|
40
|
+
// (and the server's `retainedFrom > from+1` eviction check is false → no 416 loop).
|
|
31
41
|
lastId = body.retainedFrom !== undefined ? String(body.retainedFrom - 1) : lastId;
|
|
32
42
|
if (++failures >= MAX_CONSECUTIVE_FAILURES)
|
|
33
43
|
throw toApiError(416, body);
|
|
@@ -39,14 +49,14 @@ export async function* parseSse(open, opts) {
|
|
|
39
49
|
let sawTerminal = false;
|
|
40
50
|
try {
|
|
41
51
|
for await (const frame of readSseFrames(res, grace, opts?.signal)) {
|
|
42
|
-
failures = 0;
|
|
52
|
+
failures = 0; // progress = a frame arrived (NOT just a connection accepted) — prevents an accept/drop loop
|
|
43
53
|
if (frame.id)
|
|
44
54
|
lastId = frame.id;
|
|
45
55
|
if (!frame.data)
|
|
46
|
-
continue;
|
|
47
|
-
const ev = JSON.parse(frame.data);
|
|
56
|
+
continue; // heartbeat/comment
|
|
57
|
+
const ev = JSON.parse(frame.data); // SyntaxError = contract breach → rethrown below
|
|
48
58
|
if (frame.id)
|
|
49
|
-
ev.id = frame.id;
|
|
59
|
+
ev.id = frame.id; // stamp the durable seq (BFF relays need it for resume)
|
|
50
60
|
yield ev;
|
|
51
61
|
if (ev.type === "done" || ev.type === "failed") {
|
|
52
62
|
sawTerminal = true;
|
|
@@ -57,6 +67,7 @@ export async function* parseSse(open, opts) {
|
|
|
57
67
|
catch (e) {
|
|
58
68
|
if (e instanceof SyntaxError)
|
|
59
69
|
throw new Error(`SSE: malformed event data (contract breach): ${e.message}`);
|
|
70
|
+
// mid-stream drop or heartbeat silence → resume loop (does NOT consume a transport retry)
|
|
60
71
|
}
|
|
61
72
|
finally {
|
|
62
73
|
if (!sawTerminal)
|
|
@@ -66,8 +77,16 @@ export async function* parseSse(open, opts) {
|
|
|
66
77
|
throw new Error("SSE: stream keeps ending without progress or a terminal event");
|
|
67
78
|
if (opts?.signal?.aborted)
|
|
68
79
|
return;
|
|
80
|
+
// stream ended without a terminal event → reconnect and tail on
|
|
69
81
|
}
|
|
70
82
|
}
|
|
83
|
+
/** Generic RESUMABLE frame reader — the vocabulary-agnostic sibling of `parseSse`. Unlike `parseSse` (which is
|
|
84
|
+
* hardcoded to the AgentEvent union + its terminal `done`/`failed`), this yields RAW `SseFrame`s so a caller with
|
|
85
|
+
* a DIFFERENT SSE vocabulary (the trace relay's `block-*`/`turn`/`tool-*` frames, mapTraceEvent) can resume on
|
|
86
|
+
* drops and on a 416 eviction WITHOUT inheriting AgentEvent semantics. Reconnect is driven by the last seen
|
|
87
|
+
* `id:` (Last-Event-ID); a terminal frame is the CALLER's concern (it decides what `event` ends the stream — so
|
|
88
|
+
* pass `isTerminal`). Heartbeat/comment frames (no `data`) are yielded too (the caller filters). 416 → read
|
|
89
|
+
* `retainedFrom` and resume from there (the consumer full-syncs missed ground truth via the turns API). */
|
|
71
90
|
export async function* parseFrameStream(open, isTerminal, opts) {
|
|
72
91
|
const grace = opts?.heartbeatGraceMs ?? HEARTBEAT_GRACE_MS;
|
|
73
92
|
let lastId;
|
|
@@ -97,7 +116,7 @@ export async function* parseFrameStream(open, isTerminal, opts) {
|
|
|
97
116
|
let sawTerminal = false;
|
|
98
117
|
try {
|
|
99
118
|
for await (const frame of readSseFrames(res, grace, opts?.signal)) {
|
|
100
|
-
failures = 0;
|
|
119
|
+
failures = 0; // progress = a frame arrived
|
|
101
120
|
if (frame.id)
|
|
102
121
|
lastId = frame.id;
|
|
103
122
|
yield frame;
|
|
@@ -110,6 +129,7 @@ export async function* parseFrameStream(open, isTerminal, opts) {
|
|
|
110
129
|
catch (e) {
|
|
111
130
|
if (e instanceof SyntaxError)
|
|
112
131
|
throw e;
|
|
132
|
+
// mid-stream drop or heartbeat silence → resume loop (does NOT consume a transport retry)
|
|
113
133
|
}
|
|
114
134
|
finally {
|
|
115
135
|
if (!sawTerminal)
|
|
@@ -121,6 +141,8 @@ export async function* parseFrameStream(open, isTerminal, opts) {
|
|
|
121
141
|
return;
|
|
122
142
|
}
|
|
123
143
|
}
|
|
144
|
+
/** Single-connection SSE frame reader (WHATWG format) with a heartbeat-silence watchdog. Throws on silence
|
|
145
|
+
* (caller decides resume vs abort). Used directly by tasks.stream (transient: a drop is NOT resumed). */
|
|
124
146
|
export async function* readSseFrames(res, heartbeatGraceMs = HEARTBEAT_GRACE_MS, signal) {
|
|
125
147
|
if (!res.body)
|
|
126
148
|
throw new Error("SSE: response has no body");
|
|
@@ -144,6 +166,7 @@ export async function* readSseFrames(res, heartbeatGraceMs = HEARTBEAT_GRACE_MS,
|
|
|
144
166
|
if (line.endsWith("\r"))
|
|
145
167
|
line = line.slice(0, -1);
|
|
146
168
|
if (line === "") {
|
|
169
|
+
// dispatch
|
|
147
170
|
if (dataLines.length > 0 || frame.id || frame.event) {
|
|
148
171
|
if (dataLines.length > 0)
|
|
149
172
|
frame.data = dataLines.join("\n");
|
|
@@ -154,7 +177,7 @@ export async function* readSseFrames(res, heartbeatGraceMs = HEARTBEAT_GRACE_MS,
|
|
|
154
177
|
continue;
|
|
155
178
|
}
|
|
156
179
|
if (line.startsWith(":"))
|
|
157
|
-
continue;
|
|
180
|
+
continue; // comment / heartbeat
|
|
158
181
|
const colon = line.indexOf(":");
|
|
159
182
|
const field = colon === -1 ? line : line.slice(0, colon);
|
|
160
183
|
let value = colon === -1 ? "" : line.slice(colon + 1);
|
|
@@ -173,6 +196,20 @@ export async function* readSseFrames(res, heartbeatGraceMs = HEARTBEAT_GRACE_MS,
|
|
|
173
196
|
reader.releaseLock();
|
|
174
197
|
}
|
|
175
198
|
}
|
|
199
|
+
// ── NDJSON line reader (2c session-sync `GET …/sync/entries`) — DISTINCT from SSE ────────────────────────────────
|
|
200
|
+
// The entry stream is application/x-ndjson: ONE JSON value per `\n`-terminated line, NOT the SSE `data:`/blank-line
|
|
201
|
+
// frame grammar. The framing the service emits (server.ts:2896-2898) is a sentinel envelope:
|
|
202
|
+
// {"__sync":"begin","sessionId":"<id>"}\n ← header line
|
|
203
|
+
// <SessionTreeEntry JSON>\n × N ← one entry per line
|
|
204
|
+
// {"__sync":"end","count":N}\n ← trailer
|
|
205
|
+
// The status is committed at 200 BEFORE any line, so a mid-stream failure can ONLY end the body WITHOUT the trailer —
|
|
206
|
+
// the trailer's ABSENCE (or a count mismatch) is the SOLE truncation signal (there is no 5xx mid-stream). This reader
|
|
207
|
+
// enforces that contract: it yields each entry, and THROWS `SyncTruncatedError` if the trailer is missing or its
|
|
208
|
+
// `count` ≠ the number of entries seen. It does NOT resume (unlike parseSse) — a resume is the caller's concern via
|
|
209
|
+
// the `afterSeq` cursor on a fresh GET (same-backend only).
|
|
210
|
+
/** A `/sync/entries` NDJSON stream ended without its `{"__sync":"end",count}` trailer, or the trailer's count did
|
|
211
|
+
* not match the entries received — the stream was TRUNCATED (a mid-stream server failure after the 200). Client
|
|
212
|
+
* action: re-GET (optionally with `afterSeq` to resume from the last durable seq seen, same-backend only). */
|
|
176
213
|
export class SyncTruncatedError extends Error {
|
|
177
214
|
received;
|
|
178
215
|
expected;
|
|
@@ -183,6 +220,11 @@ export class SyncTruncatedError extends Error {
|
|
|
183
220
|
this.name = "SyncTruncatedError";
|
|
184
221
|
}
|
|
185
222
|
}
|
|
223
|
+
/** Parse a 2c session-sync NDJSON entry stream from a (200) Response into the entry objects, validating the
|
|
224
|
+
* begin/end sentinel framing. Yields each entry line (the `__sync` header/trailer lines are consumed, NOT yielded).
|
|
225
|
+
* Throws {@link SyncTruncatedError} on a missing trailer or a count mismatch; rethrows a malformed-JSON line as a
|
|
226
|
+
* contract breach. The caller owns the Response (already status-checked: a non-200 is a JSON error body, handled
|
|
227
|
+
* before this is called). `T` defaults to `unknown` — the cloud re-validates the tree, so the SDK keeps entries opaque. */
|
|
186
228
|
export async function* parseNdjsonEntries(res, signal) {
|
|
187
229
|
if (!res.body)
|
|
188
230
|
throw new Error("sync: response has no body");
|
|
@@ -195,7 +237,7 @@ export async function* parseNdjsonEntries(res, signal) {
|
|
|
195
237
|
const handleLine = function* (line) {
|
|
196
238
|
const trimmed = line.trim();
|
|
197
239
|
if (trimmed.length === 0)
|
|
198
|
-
return;
|
|
240
|
+
return; // tolerate blank lines / a trailing newline
|
|
199
241
|
let v;
|
|
200
242
|
try {
|
|
201
243
|
v = JSON.parse(trimmed);
|
|
@@ -229,12 +271,15 @@ export async function* parseNdjsonEntries(res, signal) {
|
|
|
229
271
|
yield* handleLine(line);
|
|
230
272
|
}
|
|
231
273
|
}
|
|
274
|
+
// any trailing partial line with no final newline is still a complete line to honor.
|
|
232
275
|
if (buf.length > 0)
|
|
233
276
|
yield* handleLine(buf);
|
|
234
277
|
}
|
|
235
278
|
finally {
|
|
236
279
|
reader.releaseLock();
|
|
237
280
|
}
|
|
281
|
+
// The trailer is the ONLY truncation signal (the 200 was already committed). Its absence — or a count mismatch —
|
|
282
|
+
// means the body ended early (a mid-stream server failure). `begin` without `end` is likewise truncated.
|
|
238
283
|
if (trailer === null) {
|
|
239
284
|
throw new SyncTruncatedError(sawBegin ? "sync: entry stream truncated (missing trailer)" : "sync: entry stream truncated (no begin/end frame)", count);
|
|
240
285
|
}
|
package/dist/sse.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"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"}
|
package/dist/sync.d.ts
CHANGED
|
@@ -1,34 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2c session-sync — the PURE §7 relation classifier, mirrored from the service src/session-sync.ts:137
|
|
3
|
+
* (`classifySyncRelationshipByIds`). The LOCAL peer (this SDK) calls it to decide fast-forward / fork / stale
|
|
4
|
+
* BEFORE it pulls the entry stream or pushes a bundle, exactly as the cloud's `/sync/plan` + import Phase-A do
|
|
5
|
+
* server-side. Comparing the entry-ID SETS is the sound divergence test: ids are copied VERBATIM across backends
|
|
6
|
+
* (fork/import re-seq only `seq`; id/parentId/payload are byte-identical), so a set comparison is exact — it fixes
|
|
7
|
+
* the v1 leaf_id-only check that was unsound both ways (a cross-end rewind onto a shared old id false-NEGATIVEd a
|
|
8
|
+
* real fork = data loss; a clean fast-forward onto a new leaf false-POSITIVEd a conflict).
|
|
9
|
+
*
|
|
10
|
+
* No I/O, no dependencies — a byte-faithful copy so the local peer and the cloud agree on the relation without a
|
|
11
|
+
* round-trip. `srcIds`/`dstIds` are OLDEST-FIRST so the fork `commonAncestor` (the deepest shared id) and the
|
|
12
|
+
* ordered exclusive/new lists come out correct.
|
|
13
|
+
*/
|
|
1
14
|
import type { SessionManifest, SessionBundle, SyncConflictRelation, SyncRelation } from "./types.js";
|
|
2
15
|
import type { SessionSyncResource } from "./resources/session-sync.js";
|
|
16
|
+
/** §7 — classify how a SOURCE log (`srcIds`, oldest-first) relates to a DESTINATION log (`dstIds`, oldest-first, or
|
|
17
|
+
* `null`/`[]` when the dst has no such session). Returns the {@link SyncRelation} discriminated union. */
|
|
3
18
|
export declare function classifySyncRelationshipByIds(srcIds: string[], dstIds: string[] | null): SyncRelation;
|
|
19
|
+
/** The minimal structural peer both `AgentClient` ends satisfy — keeps this module free of the client graph. */
|
|
4
20
|
export interface SessionSyncPeer {
|
|
5
21
|
sessions: {
|
|
6
22
|
sync: SessionSyncResource;
|
|
7
23
|
};
|
|
8
24
|
}
|
|
9
25
|
export interface PushSessionOptions {
|
|
26
|
+
/** Echoed into import Phase A. REQUIRED to apply a `fork`/`stale` push (dst history would be lost);
|
|
27
|
+
* never defaulted — the caller must have shown the divergence to a human first. */
|
|
10
28
|
resolution?: "overwrite-dst";
|
|
11
29
|
signal?: AbortSignal;
|
|
12
30
|
}
|
|
13
31
|
export interface PushSessionResult {
|
|
32
|
+
/** The relation that decided the outcome: the Phase-B in-txn re-classify when pushed, else the dry-run plan. */
|
|
14
33
|
relation: SyncRelation["relation"];
|
|
34
|
+
/** true = entries were committed into dst (or dst already held them verbatim on `identical`). */
|
|
15
35
|
pushed: boolean;
|
|
36
|
+
/** Set when the dry-run plan classified `fork`/`stale` and no `overwrite-dst` was given — the caller
|
|
37
|
+
* surfaces the divergence (srcExclusive/dstExclusive/dstAheadBy) and may re-push with the resolution. */
|
|
16
38
|
conflict?: SyncConflictRelation;
|
|
39
|
+
/** Entries streamed into dst (0 when not pushed / identical). */
|
|
17
40
|
entryCount: number;
|
|
41
|
+
/** Content-addressed blobs uploaded to dst (idempotent PUTs; re-push re-PUTs harmlessly). */
|
|
18
42
|
blobsPushed: number;
|
|
43
|
+
/** The src manifest the push was planned from (leafId → the caller's post-push sync watermark). */
|
|
19
44
|
manifest: SessionManifest;
|
|
45
|
+
/** The FULL dry-run plan relation (fast_forward's `newEntryIds` = the tail the dst lacked — the sema
|
|
46
|
+
* shell's pull uses it to append exactly the new turns to its CC transcript). */
|
|
20
47
|
plan: SyncRelation;
|
|
21
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* PUSH one session from peer `src` into peer `dst` (same-id, cross-backend):
|
|
51
|
+
* src manifest → dst dry-run plan (§7 classify) → [conflict? return, don't write] → blobs (content-addressed
|
|
52
|
+
* PUTs) → import Phase A (small metadata + lease/active-run guards) → Phase B (pipe the src entry stream
|
|
53
|
+
* straight into the staged import — never materializes the whole log in memory beyond the NDJSON body).
|
|
54
|
+
*
|
|
55
|
+
* Conflict contract (③): a `fork`/`stale` seen at plan time is RETURNED (`{pushed:false, conflict}`) so the
|
|
56
|
+
* caller can render the divergence; a TOCTOU flip after the plan (dst advanced mid-push) THROWS the typed
|
|
57
|
+
* SyncConflictError from Phase A/B — same data-loss guard, later gate. `identical` short-circuits to a no-op.
|
|
58
|
+
* A 409 `import_in_flight`/`session_active` ConflictError from Phase A propagates as-is (retry after it settles).
|
|
59
|
+
*/
|
|
22
60
|
export declare function pushSessionBundle(src: SessionSyncPeer, dst: SessionSyncPeer, sessionId: string, opts?: PushSessionOptions): Promise<PushSessionResult>;
|
|
23
61
|
export interface PullSessionOptions {
|
|
62
|
+
/** Also fetch every referenced snapshot blob (default false — transcript-landing callers don't need the
|
|
63
|
+
* workspace bytes; engine-landing callers use `pushSessionBundle` with the clients swapped instead). */
|
|
24
64
|
includeBlobs?: boolean;
|
|
25
65
|
signal?: AbortSignal;
|
|
26
66
|
}
|
|
27
67
|
export interface PulledSession {
|
|
28
68
|
manifest: SessionManifest;
|
|
69
|
+
/** The materialized portable state (entries verbatim, oldest-first). */
|
|
29
70
|
bundle: SessionBundle;
|
|
71
|
+
/** hash → raw bytes, only when `includeBlobs` (deduped across snapshots). */
|
|
30
72
|
blobs: Map<string, Uint8Array>;
|
|
31
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* PULL one session OUT of peer `src` into memory (manifest + full entry log [+ blobs]) for callers that land
|
|
76
|
+
* the state outside an engine — e.g. the sema shell converting entries into its CC-format `<id>.jsonl`.
|
|
77
|
+
* 🔴 This MATERIALIZES the whole entry log (unlike the push pipe) — the caller owns the memory trade-off.
|
|
78
|
+
* A truncated entry stream throws SyncTruncatedError (the NDJSON trailer guard) — never a silently short log.
|
|
79
|
+
*/
|
|
32
80
|
export declare function pullSessionBundle(src: SessionSyncPeer, sessionId: string, opts?: PullSessionOptions): Promise<PulledSession>;
|
|
81
|
+
/**
|
|
82
|
+
* APPLY a materialized pull into peer `dst` (the pull counterpart of `pushSessionBundle` when the caller
|
|
83
|
+
* already holds the bundle — e.g. the sema shell pulls ONCE off the cloud, lands the entries into its CC
|
|
84
|
+
* transcript AND imports the same bundle into the local engine without re-fetching). Same order + conflict
|
|
85
|
+
* contract as pushSessionBundle: dry-run plan → [conflict? return, don't write] → blob PUTs (from the pulled
|
|
86
|
+
* `blobs` map; a snapshot hash missing from the map is skipped — the dst may already hold it, and Phase A's
|
|
87
|
+
* 422 `missing_blob` stays the honest gate) → import Phase A → Phase B from the in-memory entry array.
|
|
88
|
+
*/
|
|
33
89
|
export declare function importSessionBundle(dst: SessionSyncPeer, pulled: Pick<PulledSession, "manifest" | "bundle" | "blobs">, opts?: PushSessionOptions): Promise<PushSessionResult>;
|
|
34
90
|
//# sourceMappingURL=sync.d.ts.map
|
package/dist/sync.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,oBAAoB,EAEpB,YAAY,EACb,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAEvE;2GAC2G;AAC3G,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,YAAY,CAkCrG;AAoBD,gHAAgH;AAChH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE;QAAE,IAAI,EAAE,mBAAmB,CAAA;KAAE,CAAC;CACzC;AAED,MAAM,WAAW,kBAAkB;IACjC;wFACoF;IACpF,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,gHAAgH;IAChH,QAAQ,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;IACnC,iGAAiG;IACjG,MAAM,EAAE,OAAO,CAAC;IAChB;8GAC0G;IAC1G,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,WAAW,EAAE,MAAM,CAAC;IACpB,mGAAmG;IACnG,QAAQ,EAAE,eAAe,CAAC;IAC1B;sFACkF;IAClF,IAAI,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CA+D5B;AAED,MAAM,WAAW,kBAAkB;IACjC;6GACyG;IACzG,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,wEAAwE;IACxE,MAAM,EAAE,aAAa,CAAC;IACtB,6EAA6E;IAC7E,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,eAAe,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,aAAa,CAAC,CAyBxB;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,GAAG,QAAQ,GAAG,OAAO,CAAC,EAC5D,IAAI,CAAC,EAAE,kBAAkB,GACxB,OAAO,CAAC,iBAAiB,CAAC,CAkD5B"}
|