@tangle-network/sandbox-cli 0.22.103 → 0.22.105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -3
- package/SKILL.md +29 -0
- package/dist/graph-native-Cl6SphYB.mjs +1 -0
- package/dist/index.mjs +34 -36
- package/hub-reference.md +10 -1
- package/package.json +12 -9
- package/dist/graph-native-af-gs1TS.mjs +0 -1
package/README.md
CHANGED
|
@@ -87,10 +87,190 @@ Progress goes to stderr; `--json` returns the result or the shared error contrac
|
|
|
87
87
|
on stdout. Local run journals live under `~/.tangle/runs/<run-id>`, and local graph history
|
|
88
88
|
lives in `~/.tangle/graph-registry`. Unknown token and cost totals remain `null`.
|
|
89
89
|
|
|
90
|
+
To learn from retained runs, admit related tasks under the same independent unit
|
|
91
|
+
and keep each unit in one partition. Repeating ingestion updates that run's
|
|
92
|
+
checkpoint without adding another example. Unavailable runs remain visible with
|
|
93
|
+
evidence gaps; lifecycle completion never becomes a quality label.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
tangle --json graph dataset ./runs <run-id> --domain software --unit repository-a --split train
|
|
97
|
+
tangle --json graph dataset ./runs <another-run-id> --domain software --unit repository-b --split dev
|
|
98
|
+
tangle --json graph dataset ./runs <heldout-run-id> --domain software --unit repository-c --split test
|
|
99
|
+
tangle --json graph improve ./delivery.graph.yaml ./runs --suite ./software-evaluation.json --campaign ./campaign
|
|
100
|
+
tangle --json graph activate ./campaign ./delivery.graph.yaml
|
|
101
|
+
tangle --json graph observe ./campaign <subsequent-run-id> --unit new-production-task
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For local tasks that depend on repository files, first capture one explicit Git root while its other writers are idle. The capture uses Runtime's maintained archive and limits. It retains HEAD, the index, tracked files and unignored untracked files in private local artifacts. Keep the reference outside the repository:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
tangle --json graph workspace capture ./repository --output ./task-workspace.json
|
|
108
|
+
tangle --json graph run ./factory.graph.yaml --task-workspace ./task-workspace.json --param task=example
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Dataset ingestion retains that exact workspace reference, and learning materializes it into every fresh candidate's private directory. Retries reuse the same allocation and preserve agent edits; a standalone dry-run releases only its own fresh validation copy. Missing or corrupt archive bytes refuse fresh replay; capture never executes repository scripts. Environment variables, installed tools and Git-ignored files are not snapshotted. Tracked files and Git history may contain private data; these artifacts remain local, and cloud dispatch refuses these local-only references. Legacy runs without captures retain a `task-workspace-not-snapshotted` gap rather than acquiring an unrelated current workspace.
|
|
112
|
+
|
|
113
|
+
Datasets retain exact trigger payloads and captured MCP host policies. Legacy manifests that omitted trigger context must be reingested before learning. `dispatch`, `improve` and `repair` accept `--mcp-policy` and private `--mcp-secret key=ENVIRONMENT_VARIABLE` bindings; secret values and environment variable names are not campaign identities.
|
|
114
|
+
|
|
115
|
+
Each graph, including the proposer, reserves its complete declared ceiling through Runtime's budget pool and reconciles only its matching retained settlement. Restart verifies every previously hashed settlement and replays its actual spend through the same owner pool, preserving cache and dollar completeness; missing or inconsistent historical receipts hold. An interrupted reservation keeps the exact run ID, input and compiled graph during the owner's resume attempt. An unavailable native attachment or unknown usage cannot authorize replacement work or a new comparison. Eval may close an interrupted proposal as failed even when its native evidence is recovered. Recovered final-test cells with incomplete end-to-end latency hold the operational comparison instead of appearing artificially fast. Supplied benchmark arms use content-addressed source snapshots throughout a campaign.
|
|
116
|
+
|
|
117
|
+
Activation locks the canonical target across campaigns and rechecks its bytes after evaluator calibration and immediately before replacement. Atomic rename does not provide a filesystem compare-and-swap against unrelated editors. Activation also recompiles each measured case with ordinary execution settings and refuses a seat override or changed default that would not carry into an ordinary run. It freezes measured harness/model/seat/connection selections and MCP policy from retained compiled graph records. Observations verify those frozen host selections without consulting today's defaults, while allowing new task inputs through the same authored source. Missing execution records, historical activation records without this binding, and previously unmeasured selections refuse observation. Observations must start after activation, exclude every campaign execution, and identify a new production task unit with inputs distinct from the learning cohort and earlier observations. Repeating the same observation returns its retained verdict; another run cannot count that unit or task again.
|
|
118
|
+
|
|
119
|
+
The evaluation suite is JSON with `version: 1`, `domain`, the inline-prompt
|
|
120
|
+
`node` to improve, `objective`, `minimumEffect` and `reps`. Its `evaluator`
|
|
121
|
+
declares an absolute executable `command`, `args`, `files` (every script, rubric and data dependency)
|
|
122
|
+
and `timeoutMs`. The command reads one JSON request from stdin and emits exactly
|
|
123
|
+
`{"quality": 0.0, "notes": "independent outcome evidence"}` on stdout. Requests
|
|
124
|
+
contain `scenario`, retained `evidence`, and the candidate `artifact`, including
|
|
125
|
+
its native run ID, output, measured usage and duration. The evaluator runs outside
|
|
126
|
+
the candidate's workspace. Include `calibration` entries with a `request` and
|
|
127
|
+
independently known `quality` of zero or one; both classes must pass before
|
|
128
|
+
inference begins. A failed evaluator is an unmeasured cell, never a quality zero.
|
|
129
|
+
Evaluator commands follow the operator's subscription-only spending policy.
|
|
130
|
+
|
|
131
|
+
The suite's `budget` requires total `maxTokens`, `maxIterations`, `deadlineMs`,
|
|
132
|
+
`maxExpansions`, plus `proposalTokens`, `proposalIterations` and
|
|
133
|
+
`proposalDeadlineMs`. The graph also declares finite per-run budgets. Runtime's
|
|
134
|
+
existing reflective proposer generates detached prompt candidates, and its search
|
|
135
|
+
owns train/selection/final-test partitions, paired decisions and the closed
|
|
136
|
+
ledger. A single conserved Runtime pool bounds native candidate and proposal
|
|
137
|
+
calls; interruption retains the pending ceiling conservatively. Reusing a
|
|
138
|
+
finished campaign returns its exact retained result. Subscription-only execution forbids provider billing; an observed billed charge holds further work. Operating costs remain explicitly unknown.
|
|
139
|
+
The suite also requires `operatingConstraints.maxMeanTokens` and
|
|
140
|
+
`operatingConstraints.maxMeanDurationMs`. Only candidates with a fully matched
|
|
141
|
+
final-test cohort, known reported tokens, and measured end-to-end cell latency
|
|
142
|
+
including queueing can enter Eval's quality/token/latency frontier. Activation
|
|
143
|
+
requires those ceilings as well as the independent quality decision. Setting
|
|
144
|
+
`operatingConstraints.requireKnownOperatingCost` to true holds while environment
|
|
145
|
+
and subscription allocation costs are unknown; the report does not claim a
|
|
146
|
+
dollar frontier. Evaluator bytes are checked before and after every score and
|
|
147
|
+
again before the decision is retained.
|
|
148
|
+
|
|
149
|
+
Activation requires Runtime's independent final-test promotion, unchanged
|
|
150
|
+
evaluator dependencies, the exact measured candidate bytes and the original
|
|
151
|
+
baseline. A hold leaves the graph unchanged. Subsequent runs are graded into
|
|
152
|
+
separate observation records. The selected change is one authored inline
|
|
153
|
+
`prompt.systemPrompt`, otherwise `prompt.appendSystemPrompt`, compiled separately
|
|
154
|
+
for each task. An unchanged candidate preserves the original source bytes.
|
|
155
|
+
Full-profile, structure, code and Knowledge candidate generation stay with their
|
|
156
|
+
existing owners and are not activated by this command.
|
|
157
|
+
|
|
158
|
+
The optional suite `optimizer` defaults to `{ "kind": "native" }`. Set `kind` to
|
|
159
|
+
`gepa` and choose `recipe.kind: "engine"`, with `recipe.run.maxEvaluations`, or
|
|
160
|
+
`recipe.kind: "omni"`, with `recipe.explore` runs and `recipe.continueWith`.
|
|
161
|
+
Both use Runtime's official GEPA implementation, the same retained proposal runs,
|
|
162
|
+
independent grader and campaign pool. Supply the request, response, candidate,
|
|
163
|
+
evidence and model-request bounds declared in the [maintained optimizer schema](src/lib/graph-optimizer.ts).
|
|
164
|
+
Campaign work is serialized by the retained-attempt contract; larger engine/Omni worker counts refuse.
|
|
165
|
+
GEPA resume requires an explicit compatible-state policy and `trustLocalState`;
|
|
166
|
+
Omni requires `resume: "never"`. These adapters accept ordered text reflection
|
|
167
|
+
requests through an explicit native JSON-content protocol. They refuse tools,
|
|
168
|
+
images and unsupported model controls before dispatch. The legacy js-issues replay remains available
|
|
169
|
+
without `--suite`; it matches seats across arms and accepts total replay
|
|
170
|
+
`--max-tokens`, `--max-iterations` and `--deadline-ms` ceilings.
|
|
171
|
+
|
|
172
|
+
Native graph nodes can use Knowledge through Runtime's existing authenticated
|
|
173
|
+
coordination MCP. Declare only the tools the node needs in `profile.tools`:
|
|
174
|
+
`agent_runtime_coordination_knowledge_search`, `..._knowledge_read`,
|
|
175
|
+
`..._knowledge_record`, and `..._knowledge_resolve`. Declare a native graph input
|
|
176
|
+
`knowledge: { default: null }` to make the binding optional. Only `null` or an omitted binding disables Knowledge; every other value must pass the existing strict local/Hub binding schema. The local binding
|
|
177
|
+
comes from `tangle --json graph knowledge bind ./knowledge-store`; supply that
|
|
178
|
+
JSON as `--param knowledge=<JSON>`. It pins the curated store's exact hash, reads
|
|
179
|
+
that view, and writes proposed changes only to private run stores. A changed
|
|
180
|
+
curated hash refuses retrieval rather than silently selecting new knowledge.
|
|
181
|
+
|
|
182
|
+
Hosted bindings use `{"kind":"hub","references":[{"sourceId":"...","generation":"kg_..."}]}`.
|
|
183
|
+
The workflow owner materializes those exact references for the admitted native
|
|
184
|
+
node's sandbox and uses Knowledge's existing live credential, attachment and
|
|
185
|
+
connection checks. Each returned lexical retrieval retains a Knowledge receipt,
|
|
186
|
+
canonical visibility, source identities, generation, query and trusted Runtime
|
|
187
|
+
run/node/profile identity before delivering the result. Hosted vector retrieval
|
|
188
|
+
with graph receipts refuses before embedding calls until that spending has an
|
|
189
|
+
enforced budget. Hosted `knowledge_record` retains an isolated, unevaluated
|
|
190
|
+
proposal artifact and leaves published generations unchanged.
|
|
191
|
+
|
|
192
|
+
An independently measured local candidate reference from the maintained Knowledge
|
|
193
|
+
improvement API can be applied with `graph knowledge promote <binding.json>
|
|
194
|
+
<candidate.json>` or restored with `graph knowledge restore`. Knowledge verifies
|
|
195
|
+
the exact baseline, candidate and evaluation identities and performs its durable
|
|
196
|
+
transaction. The command returns the next exact binding. Hub generation
|
|
197
|
+
activation through this file-backed candidate API is explicitly unsupported;
|
|
198
|
+
Hub source publication remains with its existing owner. Retrieval and a proposed
|
|
199
|
+
write do not establish an improvement.
|
|
200
|
+
|
|
90
201
|
`--cloud` saves the same source through Hub Workflows and returns its run ID
|
|
91
|
-
immediately.
|
|
92
|
-
|
|
93
|
-
|
|
202
|
+
immediately. Later runs reuse that named deployment without changing its triggers.
|
|
203
|
+
Changed source or bindings require `graph apply` first. The Workflow API owns
|
|
204
|
+
execution, retained evidence and recovery; closing the CLI leaves the cloud run
|
|
205
|
+
running. `inspect` reads that same run and prints the shared workflow viewer URL
|
|
206
|
+
outside JSON mode.
|
|
207
|
+
|
|
208
|
+
Publish without starting a run, then run the exact deployed revision:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
tangle graph apply haiku-review --deploy ./deploy.yaml --dry-run --json
|
|
212
|
+
tangle graph apply haiku-review --deploy ./deploy.yaml --json
|
|
213
|
+
tangle graph run haiku-review --workflow <workflow-id> --json
|
|
214
|
+
tangle workflows get <workflow-id>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Deployment settings use the existing Hub Workflow graph deployment contract,
|
|
218
|
+
separate from the portable graph. `--param` and `--seat` override the file's
|
|
219
|
+
defaults. The platform validates trigger availability, connections and schedules.
|
|
220
|
+
For example:
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
name: weekly-audit
|
|
224
|
+
enabled: true
|
|
225
|
+
on:
|
|
226
|
+
- schedule:
|
|
227
|
+
cron: "0 8 * * 1"
|
|
228
|
+
timezone: America/Los_Angeles
|
|
229
|
+
- webhook: {}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
A provider event can instead declare
|
|
233
|
+
`provider_event: { connection: github, event: issues, actions: [opened], repo: example/project }`.
|
|
234
|
+
The graph must consume that event's `trigger` data; declaring a subscription does
|
|
235
|
+
not map an event to a static graph parameter. Provider action tools do not imply
|
|
236
|
+
that a deployment can receive its events.
|
|
237
|
+
|
|
238
|
+
An API key's expiry also bounds any schedule it authors. Set `schedule.until`
|
|
239
|
+
within that grant, or use an owner credential whose authority covers the intended
|
|
240
|
+
schedule. Dry-run reports `schedule_outlives_grant` before publication when it does
|
|
241
|
+
not; a browser login's short-lived CLI key does not grant indefinite recurrence.
|
|
242
|
+
|
|
243
|
+
`apply` matches one workflow by name, or `--workflow <id>` selects it explicitly.
|
|
244
|
+
An unchanged resolved graph and deployment create no revision for manual, webhook
|
|
245
|
+
or scheduled jobs. Provider-event apply refreshes the owner's ingress bindings,
|
|
246
|
+
including after a reconnect. First publication reserves
|
|
247
|
+
one paused, manual graph atomically, then applies the requested revision and its
|
|
248
|
+
triggers. A failed first update leaves that reservation paused and retryable. Concurrent
|
|
249
|
+
different updates conflict instead of overwriting one another. Use distinct names
|
|
250
|
+
for different deployed jobs. Deployment settings contain references and policy,
|
|
251
|
+
never credential values.
|
|
252
|
+
|
|
253
|
+
After configuring a webhook, explicitly mint its bearer with
|
|
254
|
+
`tangle graph webhook <workflow-id> --rotate`. The JSON response reveals the new
|
|
255
|
+
bearer once and retires the previous one; keep it in the receiver's secret store.
|
|
256
|
+
It is not written into graph source or deployment settings.
|
|
257
|
+
|
|
258
|
+
For local recurring work, an OS scheduler or webhook receiver owns the trigger
|
|
259
|
+
and calls the same durable local executor:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
tangle graph dispatch weekly-audit --event ./event.json --delivery schedule:2026-11-16T08:00:00-08:00 --json
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The event file is a JSON object visible as `trigger` in the graph. Use the same
|
|
266
|
+
delivery id for retries and the nominal scheduled occurrence, not the retry time.
|
|
267
|
+
The private `~/.tangle/deliveries` record binds that delivery to one Runtime run,
|
|
268
|
+
source, workspace, event and parameters. Retries resume that run. A completed
|
|
269
|
+
result replays without starting another native execution. Changed event or graph
|
|
270
|
+
bytes refuse the reused id; concurrent delivery attempts refuse while the owner
|
|
271
|
+
holds the delivery lock. A failed terminal run requires inspection and an explicit
|
|
272
|
+
new delivery. Local dispatch does not install a daemon or expose a public webhook;
|
|
273
|
+
the scheduler/receiver must remain running and manage provider authentication.
|
|
94
274
|
|
|
95
275
|
`--resume` restores saved inputs and rejects changed source, parameters, seat
|
|
96
276
|
settings or placement. A completed winner returns its retained result without
|
|
@@ -360,3 +540,18 @@ the credential's verified attribution and pins it server-side. Reads
|
|
|
360
540
|
(`sandbox list`, `sandbox get`) surface it as the read-only `compliance:
|
|
361
541
|
{ hipaa: true }` echo field — absent means the platform made no claim, and
|
|
362
542
|
`hipaa: false` never appears.
|
|
543
|
+
|
|
544
|
+
A graph deployment can bind an owner Sandbox template or snapshot without changing the graph source:
|
|
545
|
+
|
|
546
|
+
```yaml
|
|
547
|
+
name: crm-audit
|
|
548
|
+
mcpPolicy:
|
|
549
|
+
allowedMcpHosts: [crm.example.com]
|
|
550
|
+
sandbox:
|
|
551
|
+
templateId: my-owner-template
|
|
552
|
+
secrets: [CRM_TOKEN]
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
`secrets` names entries already stored in the owner's Sandbox vault. A profile's MCP `secret-ref.key` uses that same name; the private sidecar materializer resolves its value. Apply and cloud run never read or upload local environment values. Missing or unselected refs fail before native dispatch. Select `publicTemplateId` with optional `publicTemplateVersionId`, or both `fromSandboxId` and `fromSnapshot`, instead of `templateId`. Sandbox checks source ownership and capabilities through its normal creation API. A recovered Runtime checkpoint takes precedence over the deployment's initial source.
|
|
556
|
+
|
|
557
|
+
An isolated check opts into the same Sandbox binding with `environment: deployment`; its shell uses the restored `/home/agent`. Built-in `universal` and `agent-container` checks keep their existing placement. Check secrets still use the graph repository credential contract. Custom scripts needing other credentials run through the admitted native Sandbox or MCP tool path; isolated checks receive no owner vault grants.
|
package/SKILL.md
CHANGED
|
@@ -73,8 +73,37 @@ tangle graph run reviewed-change
|
|
|
73
73
|
tangle --json graph run reviewed-change --cloud
|
|
74
74
|
tangle --json graph inspect <run-id>
|
|
75
75
|
tangle graph run reviewed-change --resume <run-id>
|
|
76
|
+
# Custom MCP host admission and private local env binding:
|
|
77
|
+
tangle graph run reviewed-change --mcp-policy host-policy.json --mcp-secret tool-account=TOOL_ACCOUNT_TOKEN
|
|
76
78
|
```
|
|
77
79
|
|
|
80
|
+
Custom MCP admission comes from the host policy file. Local `--mcp-secret key=ENV_NAME`
|
|
81
|
+
binds a declared AgentProfile secret reference without recording the environment name
|
|
82
|
+
or value in graph identities. Cloud runs refuse this local flag and use their own
|
|
83
|
+
owner-vault bindings. Echoed values are redacted before local process retention and
|
|
84
|
+
native transcript copies. A transformed native capture reports incomplete original
|
|
85
|
+
bytes and the source hash; the vendor source file stays unchanged.
|
|
86
|
+
|
|
87
|
+
For recurring or provider-driven work, apply the captured deployment first and
|
|
88
|
+
inspect its returned workflow ID/revision. Run that workflow explicitly; inspect
|
|
89
|
+
the returned run ID and retained artifacts before reporting success. The
|
|
90
|
+
[Graph factories guide](README.md#graph-factories) owns deployment bindings,
|
|
91
|
+
cron/webhook/provider triggers, local event dispatch, and evaluation suite setup.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
tangle --json graph apply reviewed-change --deploy ./deploy.yaml
|
|
95
|
+
tangle --json graph run reviewed-change --workflow <workflow-id>
|
|
96
|
+
tangle --json graph inspect <run-id> --artifacts
|
|
97
|
+
tangle --json graph inspect <run-id> --artifact <node/path> --out ./retained.log
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use `graph dataset` to retain admitted attempts, including interrupted or
|
|
101
|
+
unavailable ones. Select independent task units and train/dev/test partitions
|
|
102
|
+
before `graph improve --suite`; inspect the exact promotion or hold result before
|
|
103
|
+
`graph activate`, then use `graph observe` on subsequent work. Process completion
|
|
104
|
+
alone is not a quality label. For Knowledge tools and exact bindings, read the
|
|
105
|
+
same guide before configuring node tool permissions.
|
|
106
|
+
|
|
78
107
|
Cloud dispatch returns immediately and the Workflow API owns the same run after
|
|
79
108
|
CLI disconnect. Resume preserves recorded source and execution settings. A
|
|
80
109
|
completed winner replays retained output; running cloud work attaches by read,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash as e}from"node:crypto";import{constants as t}from"node:fs";import{isAbsolute as n,join as r,relative as i}from"node:path";import{z as a}from"zod";import{open as o,opendir as s,realpath as c}from"node:fs/promises";import*as l from"@tangle-network/agent-runtime/kernel";import{CODEX_HOME_RULE as u,STORES as d,claudeCodeRefForFile as f,codexRefForFile as p,readSessionInput as m,storeGlob as h,withinPath as g}from"@tangle-network/harness-sessions";const _=a.object({harness:a.enum([`codex`,`claude-code`]),root:a.string().refine(n),basis:a.literal(`host-jail`),nativeSessionId:a.string().min(1),runId:a.string().min(1),requestDigest:a.string().regex(/^sha256:[a-f0-9]{64}$/)}),v=a.object({externalId:a.string(),internalId:a.string(),backend:a.enum([`codex`,`claude-code`]),cwd:a.string(),metadata:a.object({effective_native_store:_,agent_profile_binding:a.object({effectiveProfileDigest:a.string()})})});function y(){let e=new Map;return{observe(t){e.set(`${t.runId}:${t.requestDigest}`,t)},close(){e.clear()},redact(t,n){for(let n of t.runs){let r=e.get(`${n.runId}:${n.requestDigest}`);if(!r||r.sessionId!==t.sessionId||r.profileDigest!==t.profileDigest||r.cwd!==t.cwd)throw Error(`Exact native execution has no trusted redaction authority`)}if(!t.runs.length)throw Error(`Native redaction requires an exact execution binding`);let r=n;for(let n of e.values())n.sessionId===t.sessionId&&n.profileDigest===t.profileDigest&&n.cwd===t.cwd&&(r=n.redact(r));return r}}}function b(e,t){if(e===`codex`)return p(t);let n=t.split(`/`),r=n.indexOf(`subagents`,3);return f(t,r>0?n[r-1]??null:null)}async function x(e,t,n){let a=[],o=d[`claude-code.projects-jsonl`],f=r(t,...o.root.split(`/`).slice(1)),p=e===`codex`?u.sessionDirs.map(e=>r(t,e)):[f],m=e===`codex`?[h(u.rollout)]:o.files.map(e=>h(e)),_=0;for(;p.length;){n.throwIfAborted();let o=p.shift();if(o===void 0)break;let u;try{u=await c(o)}catch(e){if(e.code===`ENOENT`)continue;throw e}if(!g(u,t))throw Error(`Native inventory escaped its attested store`);let d=await s(u);for await(let o of d){if(n.throwIfAborted(),++_>l.HARNESS_TRANSCRIPT_READ_LIMITS.maxFiles)throw Error(`Native inventory exceeds the capture bound`);let s=r(d.path,o.name);o.isDirectory()?p.push(s):o.isFile()&&m.some(t=>t.test(e===`codex`?o.name:i(f,s)))&&a.push({path:i(t,s),nativeSessionId:b(e,i(t,s)).nativeSessionId})}}return a}function S(n){return async i=>{if(![`codex`,`claude-code`].includes(i.harness))return{status:`unavailable`,reason:`unknown-harness`,skipped:[{path:i.harness,reason:`Native transcript capture is not configured for this harness`}]};let s=await fetch(`${n.bridgeUrl}/v1/sessions?session_id=${encodeURIComponent(i.sessionId)}&backend=${i.harness}`,{headers:{authorization:`Bearer ${n.bridgeBearer}`},signal:i.signal});if(!s.ok)throw Error(`Bridge session lookup failed (HTTP ${s.status})`);let u=a.object({data:a.tuple([v])}).parse(await s.json()).data[0],d=u.metadata.effective_native_store,f=await c(d.root);if(u.backend!==i.harness||d.harness!==i.harness||u.externalId!==i.sessionId||u.internalId!==d.nativeSessionId||u.cwd!==n.cwd||i.cwd!==n.cwd||u.metadata.agent_profile_binding.effectiveProfileDigest!==i.profileDigest||f!==d.root||!g(f,r(n.cwd,`.agent-home`))||!i.runs.some(e=>e.runId===d.runId&&e.requestDigest===d.requestDigest))throw Error(`Bridge native binding does not match this execution`);let p=[],h=[...new Set(n.privateMcpSecrets?.()??[])].flatMap(e=>[e,JSON.stringify(e).slice(1,-1)]).filter(Boolean).sort((e,t)=>t.length-e.length),_=await l.captureBoundHarnessTranscript({harness:i.harness,binding:{kind:`bridge`,sessionId:i.sessionId,nativeSessionId:d.nativeSessionId,profileDigest:i.profileDigest,runs:i.runs,...i.node===void 0?{}:{node:i.node}},nativeSessionId:d.nativeSessionId,files:await x(i.harness,f,i.signal),signal:i.signal,parse:(e,t)=>m(i.harness,{text:e},{...b(i.harness,t.path),label:t.path},{corruption:`strict`}),read:async(a,s)=>{let u=await c(r(f,a.path));if(!g(u,f))throw Error(`Native session escaped its attested store`);let d=await o(u,t.O_RDONLY|t.O_NOFOLLOW);try{let t=await d.stat();if(!t.isFile()||t.size>l.HARNESS_TRANSCRIPT_READ_LIMITS.maxFileBytes)throw Error(`Native session is not a bounded regular file`);s?.throwIfAborted();let r=await d.readFile();if(s?.throwIfAborted(),!n.redact)throw Error(`The native host did not supply private redaction authority`);let o=n.redact(i,r),c=new TextDecoder(`utf-8`,{fatal:!0}).decode(o);for(let e of h)c=c.split(e).join(`[redacted:MCP]`);let u=Buffer.from(c,`utf8`);return u.equals(r)||p.push({path:a.path,reason:`Original bytes withheld for private MCP credential redaction; source sha256:${e(`sha256`).update(r).digest(`hex`)}; retained copy is redacted`}),u}finally{await d.close()}}});return _.status!==`captured`||!p.length?_:{..._,artifact:{..._.artifact,skipped:[..._.artifact.skipped,...p]},skippedCount:_.skippedCount+p.length,coverageComplete:!1,missing:[`private-mcp-secret-original-bytes`]}}}const C=async(e,t)=>{if(![`codex`,`claude-code`].includes(e.harness)||e.binding?.kind!==`bridge`)throw Error(`Native transcript reader is not configured for this capture`);return(await l.readBoundHarnessSessions({nativeSessionId:e.binding.nativeSessionId,files:e.files.map(t=>({path:t.path,nativeSessionId:b(e.harness,t.path).nativeSessionId,byteLength:t.bytes})),read:async t=>{let n=e.files.find(e=>e.path===t.path);if(!n)throw Error(`Retained native file is absent`);return Buffer.from(n.content,`utf8`)},parse:(t,n)=>m(e.harness,{text:t},{...b(e.harness,n.path),label:n.path},{corruption:`strict`}),signal:t})).sessions};export{S as graphNativeCapture,y as graphNativeRedaction,C as readGraphSessions};
|