nexus-ai-pro 2.10.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API_STABILITY.md +74 -0
- package/CHANGELOG.md +188 -1
- package/README.md +13 -5
- package/dist/agent/files.d.ts +36 -0
- package/dist/agent/files.js +112 -0
- package/dist/agent/middleware/filesystem.d.ts +3 -3
- package/dist/agent/sandbox-container.js +6 -4
- package/dist/agent/sandbox.d.ts +17 -58
- package/dist/agent/sandbox.js +19 -229
- package/dist/agent/workspace.d.ts +220 -0
- package/dist/agent/workspace.js +673 -0
- package/dist/core/nexus.d.ts +7 -0
- package/dist/core/nexus.js +3 -0
- package/dist/deep-agent/index.d.ts +15 -6
- package/dist/deep-agent/index.js +20 -18
- package/dist/evaluate/automation.d.ts +256 -0
- package/dist/evaluate/automation.js +368 -0
- package/dist/evaluate/index.d.ts +2 -1
- package/dist/evaluate/index.js +1 -0
- package/dist/evaluate/media.d.ts +146 -0
- package/dist/evaluate/media.js +447 -0
- package/dist/evaluate/online.d.ts +25 -0
- package/dist/evaluate/online.js +29 -3
- package/dist/evaluate/trace.d.ts +21 -0
- package/dist/evaluate/trace.js +162 -0
- package/dist/media/catalog.d.ts +239 -0
- package/dist/media/catalog.js +319 -0
- package/dist/realtime/session.d.ts +6 -0
- package/dist/realtime/session.js +34 -3
- package/dist/server/deployments.d.ts +109 -7
- package/dist/server/deployments.js +221 -47
- package/dist/server/runs.d.ts +8 -0
- package/dist/server/runs.js +38 -9
- package/dist/telephony/index.d.ts +4 -2
- package/dist/telephony/index.js +2 -1
- package/dist/telephony/manager.d.ts +31 -1
- package/dist/telephony/manager.js +84 -0
- package/dist/telephony/providers/telnyx.d.ts +108 -0
- package/dist/telephony/providers/telnyx.js +646 -0
- package/dist/telephony/providers/twilio.d.ts +24 -3
- package/dist/telephony/providers/twilio.js +174 -0
- package/dist/telephony/realtime-bridge.d.ts +5 -0
- package/dist/telephony/realtime-bridge.js +1 -0
- package/dist/telephony/recordings.d.ts +91 -0
- package/dist/telephony/recordings.js +86 -0
- package/dist/telephony/twiml.d.ts +2 -1
- package/dist/telephony/twiml.js +20 -0
- package/dist/tracing/index.d.ts +1 -1
- package/dist/tracing/index.js +1 -1
- package/dist/tracing/instrument.d.ts +16 -0
- package/dist/tracing/instrument.js +29 -0
- package/dist/tracing/otlp-grpc.d.ts +61 -0
- package/dist/tracing/otlp-grpc.js +229 -0
- package/dist/tracing/otlp-proto.d.ts +24 -0
- package/dist/tracing/otlp-proto.js +192 -0
- package/dist/tracing/otlp.d.ts +69 -10
- package/dist/tracing/otlp.js +65 -14
- package/dist/tracing/spool.d.ts +111 -0
- package/dist/tracing/spool.js +245 -0
- package/dist/tracing/tracer.d.ts +8 -0
- package/dist/tracing/tracer.js +8 -1
- package/dist/types/evaluate.d.ts +106 -0
- package/dist/types/lifecycle.d.ts +1 -1
- package/dist/types/server.d.ts +29 -0
- package/dist/types/telephony.d.ts +247 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/agent/files.d.ts +36 -0
- package/dist-cjs/agent/files.js +148 -0
- package/dist-cjs/agent/middleware/filesystem.d.ts +3 -3
- package/dist-cjs/agent/sandbox-container.js +6 -4
- package/dist-cjs/agent/sandbox.d.ts +17 -58
- package/dist-cjs/agent/sandbox.js +19 -229
- package/dist-cjs/agent/workspace.d.ts +220 -0
- package/dist-cjs/agent/workspace.js +686 -0
- package/dist-cjs/core/nexus.d.ts +7 -0
- package/dist-cjs/core/nexus.js +3 -0
- package/dist-cjs/deep-agent/index.d.ts +15 -6
- package/dist-cjs/deep-agent/index.js +20 -18
- package/dist-cjs/evaluate/automation.d.ts +256 -0
- package/dist-cjs/evaluate/automation.js +377 -0
- package/dist-cjs/evaluate/index.d.ts +2 -1
- package/dist-cjs/evaluate/index.js +4 -1
- package/dist-cjs/evaluate/media.d.ts +146 -0
- package/dist-cjs/evaluate/media.js +453 -0
- package/dist-cjs/evaluate/online.d.ts +25 -0
- package/dist-cjs/evaluate/online.js +29 -3
- package/dist-cjs/evaluate/trace.d.ts +21 -0
- package/dist-cjs/evaluate/trace.js +167 -0
- package/dist-cjs/media/catalog.d.ts +239 -0
- package/dist-cjs/media/catalog.js +323 -0
- package/dist-cjs/realtime/session.d.ts +6 -0
- package/dist-cjs/realtime/session.js +34 -3
- package/dist-cjs/server/deployments.d.ts +109 -7
- package/dist-cjs/server/deployments.js +221 -47
- package/dist-cjs/server/runs.d.ts +8 -0
- package/dist-cjs/server/runs.js +38 -9
- package/dist-cjs/telephony/index.d.ts +4 -2
- package/dist-cjs/telephony/index.js +4 -1
- package/dist-cjs/telephony/manager.d.ts +31 -1
- package/dist-cjs/telephony/manager.js +84 -0
- package/dist-cjs/telephony/providers/telnyx.d.ts +108 -0
- package/dist-cjs/telephony/providers/telnyx.js +650 -0
- package/dist-cjs/telephony/providers/twilio.d.ts +24 -3
- package/dist-cjs/telephony/providers/twilio.js +174 -0
- package/dist-cjs/telephony/realtime-bridge.d.ts +5 -0
- package/dist-cjs/telephony/realtime-bridge.js +2 -0
- package/dist-cjs/telephony/recordings.d.ts +91 -0
- package/dist-cjs/telephony/recordings.js +89 -0
- package/dist-cjs/telephony/twiml.d.ts +2 -1
- package/dist-cjs/telephony/twiml.js +20 -0
- package/dist-cjs/tracing/index.d.ts +1 -1
- package/dist-cjs/tracing/index.js +2 -1
- package/dist-cjs/tracing/instrument.d.ts +16 -0
- package/dist-cjs/tracing/instrument.js +30 -0
- package/dist-cjs/tracing/otlp-grpc.d.ts +61 -0
- package/dist-cjs/tracing/otlp-grpc.js +237 -0
- package/dist-cjs/tracing/otlp-proto.d.ts +24 -0
- package/dist-cjs/tracing/otlp-proto.js +196 -0
- package/dist-cjs/tracing/otlp.d.ts +69 -10
- package/dist-cjs/tracing/otlp.js +69 -15
- package/dist-cjs/tracing/spool.d.ts +111 -0
- package/dist-cjs/tracing/spool.js +248 -0
- package/dist-cjs/tracing/tracer.d.ts +8 -0
- package/dist-cjs/tracing/tracer.js +8 -1
- package/dist-cjs/types/evaluate.d.ts +106 -0
- package/dist-cjs/types/lifecycle.d.ts +1 -1
- package/dist-cjs/types/server.d.ts +29 -0
- package/dist-cjs/types/telephony.d.ts +247 -1
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/llms.txt +1 -1
- package/package.json +92 -1
package/API_STABILITY.md
CHANGED
|
@@ -41,6 +41,80 @@ Both are covered by this policy:
|
|
|
41
41
|
Deep imports into `dist`, `dist-cjs`, or `src` are not supported. Import only from `nexus-ai-pro` or
|
|
42
42
|
one of its explicit subpaths.
|
|
43
43
|
|
|
44
|
+
## 2.12 stage (2.12.0)
|
|
45
|
+
|
|
46
|
+
Everything 2.12 adds is stable from the start and additive, unless this section says otherwise.
|
|
47
|
+
|
|
48
|
+
New and stable:
|
|
49
|
+
- `nexus-ai-pro/tracing/spool`: `spoolingExporter()`, `SpoolingExporter`, `SpoolingExporterOptions`,
|
|
50
|
+
`TraceBatchSender`, `SpoolStats`, and `SpoolDrainReport`;
|
|
51
|
+
- `OtlpTraceExporter.send()`;
|
|
52
|
+
- `RunManagerOptions.tracer` and `AgentServerOptions.tracer`, and `RunRecord.traceId`;
|
|
53
|
+
- `Deployments.evaluationWorker()` and `DeploymentEvaluationWorkerOptions`; `RunEvaluationOptions`
|
|
54
|
+
gains `traces`, `operations`, `pollMs`, and the worker's options, and `RunEvaluationReport` gains
|
|
55
|
+
`failed`;
|
|
56
|
+
- in `nexus-ai-pro/evaluate`: `traceEvidence()`, `mediaParts()`, `EvaluationTrace`, `MediaPart`,
|
|
57
|
+
`TraceToolCall`, `TraceRetrieval`, `TraceDocument`, `TraceModelCall`, `TraceMedia`, and
|
|
58
|
+
`EvaluationContext.trace`;
|
|
59
|
+
- `OnlineEvaluationWorkerOptions.withTrace`, `onScored`, and `onError`, and `OnlineScored`;
|
|
60
|
+
- `traceRetriever()`, `RetrieverLike`, and `Tracer.within()`;
|
|
61
|
+
- `nexus-ai-pro/evaluate/automation`: `automationRules()`, `deploymentScores()`, `traceScores()`,
|
|
62
|
+
`memoryScores()`, `openReview()`, `sendWebhook()`, `markUnhealthy()`, `AutomationRule`,
|
|
63
|
+
`AutomationRules`, `AutomationRulesOptions`, `AutomationEvent`, `AutomationAction`,
|
|
64
|
+
`AutomationStatus`, `AutomationWebhookOptions`, `ScoreSample`, `ScoreSource`, `MemoryScoreSource`,
|
|
65
|
+
`ReviewOpener`, and `RuleStatistic`;
|
|
66
|
+
- `Deployments.markUnhealthy()`, `markHealthy()`, and `scores()`, `RunScore`, the `health`
|
|
67
|
+
`DeploymentChange`, `DeploymentRecord.unhealthy`, `RevisionHealthMark`,
|
|
68
|
+
`DeploymentChangeRecord.revision` and `healthy`, and `CanaryDecision.unhealthy`;
|
|
69
|
+
- `nexus-ai-pro/evaluate/media`: `mediaChecks()`, `mediaJudge()`, `mediaOf()`, `resolveMedia()`,
|
|
70
|
+
`MediaCheckOptions`, `MediaJudgeOptions`, `MediaResolveOptions`, `MediaSelection`, `MediaRange`,
|
|
71
|
+
`ResolvedMedia`, and `MediaSubjectKind`;
|
|
72
|
+
- `nexus-ai-pro/media/catalog`: `mediaCatalog()`, `catalogedAssetStore()`, `MediaCatalog`,
|
|
73
|
+
`MediaCatalogOptions`, `MediaCatalogEntry`, `MediaCatalogQuery`, `MediaCatalogPage`,
|
|
74
|
+
`MediaCatalogStats`, `MediaCatalogRecordOptions`, `CatalogScore`, `CatalogMediaKind`, and
|
|
75
|
+
`CatalogedAssetStoreOptions`;
|
|
76
|
+
- `nexus-ai-pro/tracing/otlp-grpc`: `OtlpGrpcTraceExporter`, `OtlpGrpcTraceExporterOptions`, and
|
|
77
|
+
`OtlpGrpcError`;
|
|
78
|
+
- in `nexus-ai-pro/tracing/otlp`: `OtlpTraceExporterOptions.protocol`, `encodeOtlpTraceRequest()`,
|
|
79
|
+
`decodeOtlpTraceResponse()`, `OtlpExportRequest`, `OtlpKeyValue`, `OtlpAttributeValue`,
|
|
80
|
+
`OtlpPartialSuccess`, and `OtlpPartialSuccessError`.
|
|
81
|
+
|
|
82
|
+
Changed: `DeploymentChange` gains a `health` action, so a `switch` over its actions that must be
|
|
83
|
+
exhaustive needs a case for it.
|
|
84
|
+
|
|
85
|
+
Changed: `Deployments.evaluate()` runs through the online-evaluation worker, scoring up to 4 runs at
|
|
86
|
+
once rather than one, so evaluators that are not safe to call concurrently need `concurrency: 1`.
|
|
87
|
+
|
|
88
|
+
## 2.11 stage (2.11.0)
|
|
89
|
+
|
|
90
|
+
Everything 2.11 adds is stable from the start and additive, unless this section says otherwise.
|
|
91
|
+
|
|
92
|
+
New and stable:
|
|
93
|
+
- `nexus-ai-pro/agent/workspace`: `Workspace`, `WorkspaceGlobOptions`, `WorkspaceGrepOptions`,
|
|
94
|
+
`WorkspaceMatch`, `WorkspacePathError`, `WorkspaceReadOnlyError`, `resolveWorkspacePath()`,
|
|
95
|
+
`memoryWorkspace()`, `fileWorkspace()`, `storeWorkspace()`, `readOnlyWorkspace()`,
|
|
96
|
+
`compositeWorkspace()` and their options, `WorkspaceMount`, `workspaceTools()` and
|
|
97
|
+
`WorkspaceToolsOptions`, and `runWorkspaceConformance()` with its report and check;
|
|
98
|
+
- `DeepAgentOptions.workspace`;
|
|
99
|
+
- `nexus-ai-pro/telephony/telnyx`, with `TelnyxTelephonyProvider` and its config;
|
|
100
|
+
- on `nexus-ai-pro/telephony`: `TelephonyManager.answer()`, `transferCall()`, `joinConference()`,
|
|
101
|
+
`startRecording()`, `stopRecording()`, `parseRecordingCallback()`, `downloadRecording()`,
|
|
102
|
+
`deleteRecording()`, `sendMessage()`, and `parseMessageWebhook()`, the matching optional
|
|
103
|
+
`TelephonyProvider` methods, their request and result types, `TelephonyDialConfig`,
|
|
104
|
+
`TelephonyConferenceConfig`, and `TelephonyResponseRequest.dial`; `recordingKeeper()` with
|
|
105
|
+
`RecordingKeeper`, `RecordingKeeperOptions`, `RecordingRetention`, and `KeptRecording`;
|
|
106
|
+
`telnyxRealtimeAudioOptions()`; the `TelephonyProviderInfo.supports` flags `transfer`,
|
|
107
|
+
`conference`, `recording`, and `messaging`; `TelephonyWebhookValidationRequest.publicKey`;
|
|
108
|
+
- `NexusAI.telephony`; the operation family `messaging`;
|
|
109
|
+
- `Sandbox` extends `Workspace`, so it gains `writeBytes()`, optional; `SandboxGlobOptions`,
|
|
110
|
+
`SandboxGrepOptions`, and `SandboxMatch` are the workspace's types under their old names, and
|
|
111
|
+
`SandboxPathError` is a `WorkspacePathError`.
|
|
112
|
+
|
|
113
|
+
Changed, each to keep a promise the surface already made:
|
|
114
|
+
- A sandbox's file methods take a leading `/` as the sandbox's root, as the workspace contract
|
|
115
|
+
says; a path that leaves the root is still refused. The container sandbox rejects reading a
|
|
116
|
+
missing file with `ENOENT`.
|
|
117
|
+
|
|
44
118
|
## 2.10 stage (2.10.0)
|
|
45
119
|
|
|
46
120
|
Everything 2.10 adds is stable from the start and additive, unless this section says otherwise.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,191 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.12.0] - 2026-10-18
|
|
8
|
+
|
|
9
|
+
Observability that loses nothing, and media you can evaluate.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Spans that a collector being down cannot lose.** `spoolingExporter()`, on the new
|
|
14
|
+
`nexus-ai-pro/tracing/spool`, keeps each batch the collector refuses in an operation store (the
|
|
15
|
+
memory, Redis, Postgres, or SQLite one), retries it with backoff, and dead-letters it only once it is
|
|
16
|
+
too old or has failed too often, under a bound on what waits. Every replica on the store drains the
|
|
17
|
+
same spool, each batch once. `writeAhead` spools each batch before sending it. `stats()` reports
|
|
18
|
+
what was exported, retried, spooled, dropped, and dead-lettered, and the age of the oldest batch
|
|
19
|
+
waiting.
|
|
20
|
+
- `OtlpTraceExporter.send()`: sends runs as one batch now, and rejects when the collector refuses
|
|
21
|
+
them.
|
|
22
|
+
- **A deployment judged by what its runs did.** An agent server given a `tracer` makes each run the
|
|
23
|
+
root of a trace, with its input and output, and everything the assistant traces through the same
|
|
24
|
+
tracer beneath it; the run records the trace as `RunRecord.traceId`. `Deployments.evaluate()`
|
|
25
|
+
given those `traces` hands each evaluator the run's input and an `EvaluationTrace` of its tool
|
|
26
|
+
calls, retrievals, model calls, and media, read by the new `traceEvidence()`, which `mediaParts()`
|
|
27
|
+
backs. An assistant that traces itself is found by the run id it records.
|
|
28
|
+
- `Deployments.evaluationWorker()`: the online-evaluation worker over a deployment's runs, to start
|
|
29
|
+
on every replica, scoring each run once between them as it finishes.
|
|
30
|
+
- `onlineEvaluationWorker()` takes `withTrace`, which gives evaluators `context.trace`, and
|
|
31
|
+
`onScored`, which hears each run's scores once they are written, with `onError` for its failures.
|
|
32
|
+
- `traceRetriever()`: every retrieval as a `retriever` run, with the query and the documents.
|
|
33
|
+
- **Media you can evaluate.** On the new `nexus-ai-pro/evaluate/media`, `mediaChecks()` reads the
|
|
34
|
+
media an evaluation is about, from bytes, base64, a data URL, a URL it is allowed to fetch, a path,
|
|
35
|
+
or an asset store, and scores each check asked: the count, kind, MIME type, size, dimensions,
|
|
36
|
+
aspect ratio, and a WAV's duration. `mediaJudge()` shows them to a vision or audio model beside a
|
|
37
|
+
rubric and the brief, a stored asset sent as its bytes, and records the judge in an experiment.
|
|
38
|
+
`mediaOf()` finds the media in an output, or in what the run's trace says it produced, and
|
|
39
|
+
`resolveMedia()` reads one.
|
|
40
|
+
- **Automation rules.** On the new `nexus-ai-pro/evaluate/automation`, `automationRules()` judges
|
|
41
|
+
a score over each revision's most recent runs, and when it falls below a threshold over enough
|
|
42
|
+
samples (optionally by the upper bound of a confidence interval), acts once, across every replica
|
|
43
|
+
sharing its `state`: `openReview()` sends the worst runs to people, `sendWebhook()` posts a signed
|
|
44
|
+
event, and `markUnhealthy()` marks the revision unhealthy, which rolls a canary back. Rules read
|
|
45
|
+
scores a deployment's run records hold (`deploymentScores()`), a trace store's
|
|
46
|
+
(`traceScores()`), or this process's (`memoryScores()`), and `observe()` judges them as an online
|
|
47
|
+
evaluation writes them. A rule fires once per fall, recovers, and can repeat.
|
|
48
|
+
- **A revision's health.** `Deployments.markUnhealthy()` and `markHealthy()`: a `health` change in
|
|
49
|
+
the deployment's history, kept in `DeploymentRecord.unhealthy`. A canary marked unhealthy is rolled
|
|
50
|
+
back at once, and a canary guard rolls back any canary marked since it began, its `CanaryDecision`
|
|
51
|
+
carrying the mark. The studio shows each revision's health and marks one from the deployment view.
|
|
52
|
+
`Deployments.scores()` reads the scores of an assistant's runs.
|
|
53
|
+
- **A catalog of every stored asset.** `mediaCatalog()`, on the new `nexus-ai-pro/media/catalog`,
|
|
54
|
+
keeps one record of each stored asset in any long-term store (Postgres, SQLite, Redis, or memory):
|
|
55
|
+
what it is, who made it, for which run, its tags, and its latest score per key, written by
|
|
56
|
+
compare-and-set. `catalogedAssetStore()` wraps any asset store so every asset stored through it is
|
|
57
|
+
recorded and every one deleted is forgotten, an entry expiring with its asset. `list()` finds
|
|
58
|
+
entries by kind, type, provider, model, run, store, tag, time, and score, a page at a time;
|
|
59
|
+
`attach()` scores the assets a run's output refers to; `stats()`, `purgeExpired()`, and
|
|
60
|
+
`reconcile()` keep account.
|
|
61
|
+
- **OTLP over gRPC.** `OtlpGrpcTraceExporter`, on the new `nexus-ai-pro/tracing/otlp-grpc`, sends
|
|
62
|
+
spans to a collector's port 4317 with no gRPC library: the trace service's `Export` call framed by
|
|
63
|
+
hand over Node's HTTP/2 client, gzip-compressed, with metadata, a deadline, TLS options, one
|
|
64
|
+
connection kept and reopened, and a failed call rejecting with an `OtlpGrpcError` so a spool keeps
|
|
65
|
+
the batch. `OtlpTraceExporter` takes `protocol: 'http/protobuf'`, and both read a collector's
|
|
66
|
+
partial success into an `OtlpPartialSuccessError` for `onError`. `encodeOtlpTraceRequest()` and
|
|
67
|
+
`decodeOtlpTraceResponse()` are OTLP's protobuf encoding, written by hand, on any runtime.
|
|
68
|
+
- `Tracer.within()`: runs a function with a run started by `startRun()` in scope.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- `Deployments.evaluate()` runs through the online-evaluation worker, drained once, instead of beside
|
|
73
|
+
it. Replicas given one `operations` store score each run once between them, even calling at the
|
|
74
|
+
same moment, where before two calls could score a run twice; it takes the worker's budget, review
|
|
75
|
+
queue, and concurrency (4 at a time by default, one before), and `RunEvaluationReport` counts
|
|
76
|
+
`failed` evaluations. Which runs a `sampleRate` picks is unchanged.
|
|
77
|
+
|
|
78
|
+
### Proof
|
|
79
|
+
|
|
80
|
+
- `tests/trace-spool.test.ts`: a collector stopped for an hour while a span finishes every second,
|
|
81
|
+
and the process restarted halfway: every one of the 3,600 spans reaches it, once, on the memory,
|
|
82
|
+
SQLite, Postgres, and Redis operation stores. The bound, dead letters, two replicas on one spool,
|
|
83
|
+
and write-ahead through a process that dies mid-send. Without the spool, its bound, its claim, its
|
|
84
|
+
dead letters, write-ahead, or taking over a lapsed lease, a test fails.
|
|
85
|
+
- `tests/media-evaluation.test.ts`: checks that read PNG, GIF, WAV, and PDF media from a store,
|
|
86
|
+
base64, a data URL, and a fetch only when allowed; a judge model shown a stored asset as its bytes
|
|
87
|
+
beside the rubric and the brief; media found through the trace; and an experiment over a target
|
|
88
|
+
that draws. Without the sniffing, the store, the wildcard, the tolerance, the duration, the size
|
|
89
|
+
limit, the count, the media shown, the brief, or the trace fallback, a test fails.
|
|
90
|
+
- `tests/automation-rules.test.ts`: the proof that a canary is rolled back on a media evaluator's
|
|
91
|
+
score. A designer assistant's canary draws its banners square; two replicas score 80 runs with a
|
|
92
|
+
media judge shown each banner, and a rule on the judge's score marks the canary unhealthy, rolling
|
|
93
|
+
it back, opening review items for the five worst banners, and sending one signed webhook, once
|
|
94
|
+
between the replicas. The live revision is untouched, new runs go to it, and the media catalog
|
|
95
|
+
finds each of the canary's banners by its score. Then minimum samples, scopes, windows, the
|
|
96
|
+
confidence bound, the pass rate, recovery, repeats, filters, sources, failing actions, and a guard
|
|
97
|
+
rolling back a canary a rule only marked. Without the rollback, the guard's mark, the stale-mark
|
|
98
|
+
check, the claim, the minimum, the window, recovery, the bound, counting each run once, the
|
|
99
|
+
revision scope, the signature, or the repeat, a test fails.
|
|
100
|
+
- `tests/media-catalog.test.ts`: on the memory, SQLite, Postgres, and Redis stores, forty assets of
|
|
101
|
+
five kinds, two providers, and two tenants, some deleted and some expiring: every page lists each
|
|
102
|
+
remaining asset once, every filter finds exactly its assets, expired ones are never read and are
|
|
103
|
+
purged with their assets, and ten evaluators scoring one asset at once keep all ten scores. An
|
|
104
|
+
asset deleted around the catalog is reconciled away, and one the catalog cannot record is not left
|
|
105
|
+
stored. Without the forgetting, the expiry, the purge, compare-and-set, the cursor, the filters,
|
|
106
|
+
the compensation, or the reconciliation, a test fails.
|
|
107
|
+
- `tests/otlp-grpc.test.ts`: the OpenTelemetry Collector itself, in a container, takes spans over
|
|
108
|
+
gRPC and sends every one on as it came, ids, times, kinds, attributes, and status. Against a gRPC
|
|
109
|
+
server of the test's, with a protobuf reader written apart from the encoder: compression and
|
|
110
|
+
metadata, a refusal, a deadline, a partial success, a collector that is down, one that restarts
|
|
111
|
+
under a live connection, and a spool that keeps what is refused. Without the compressed flag, the
|
|
112
|
+
status, the deadline, the metadata, the parent span, the status message, typed values, the
|
|
113
|
+
reconnection, or the retry on a dropped connection, a test fails.
|
|
114
|
+
- `tests/deployment-trace-evaluation.test.ts`: a traced server whose assistant retrieves a policy,
|
|
115
|
+
calls a tool, and asks a model about a photo; its evaluators see the input, the tool call, the
|
|
116
|
+
documents, the model call, and the photo once. Two replicas evaluating at once over one operation
|
|
117
|
+
store score each of 40 runs once, where queues of their own score runs twice. A worker scores runs
|
|
118
|
+
as they finish and hears each score once, and an assistant tracing itself is found. Without the
|
|
119
|
+
trace, the input, the run scope, the shared queue, the fallback, the media deduplication, or the
|
|
120
|
+
trace written before the run settles, a test fails.
|
|
121
|
+
|
|
122
|
+
## [2.11.0] - 2026-10-18
|
|
123
|
+
|
|
124
|
+
Workspaces, and telephony in full.
|
|
125
|
+
|
|
126
|
+
### Added
|
|
127
|
+
|
|
128
|
+
- **Workspaces: where an agent's files live, apart from where its code runs.** A `Workspace`, on
|
|
129
|
+
the new `nexus-ai-pro/agent/workspace`, reads, writes, and lists files, and optionally searches
|
|
130
|
+
them, removes them, and holds bytes, with paths that mean the same in every one. Five ship:
|
|
131
|
+
`memoryWorkspace()`, `fileWorkspace()`, `storeWorkspace()` on any long-term `Store` (each file its
|
|
132
|
+
contents and a small tree entry, so listing reads no contents), `readOnlyWorkspace()`, and
|
|
133
|
+
`compositeWorkspace()`, which mounts several at paths of its own, read-only or not, and searches
|
|
134
|
+
across them. `workspaceTools()` gives the file tools over any of them, and
|
|
135
|
+
`runWorkspaceConformance()` holds any workspace to the contract. A `Sandbox` is now a workspace
|
|
136
|
+
that can also run commands. A deep agent takes `workspace` apart from `sandbox`: its files, skills,
|
|
137
|
+
instruction files, and offloaded results live in the workspace, and its commands run in the sandbox.
|
|
138
|
+
- `Workspace.writeBytes()`, optional, which the process sandbox now has.
|
|
139
|
+
- **Telephony in full, on a second provider.** `TelnyxTelephonyProvider`, on the new
|
|
140
|
+
`nexus-ai-pro/telephony/telnyx`, on Telnyx's own call-control protocol: calls placed and answered
|
|
141
|
+
by command, Ed25519-signed webhooks verified with Web Crypto, media streams that read as Twilio's
|
|
142
|
+
do, phone numbers, and everything below. `TelephonyManager.answer()` answers a call webhook the same
|
|
143
|
+
way on either provider: TwiML on Twilio, commands on Telnyx.
|
|
144
|
+
- **Transfer and conferences** on both: `transferCall()` and `joinConference()`, and a `dial` in a
|
|
145
|
+
webhook answer, rendered as `<Dial>` on Twilio.
|
|
146
|
+
- **Call recordings kept on your terms.** `startRecording()`, `stopRecording()`,
|
|
147
|
+
`parseRecordingCallback()`, `downloadRecording()`, and `deleteRecording()` on both, and
|
|
148
|
+
`recordingKeeper()`, which keeps each finished recording in an `AssetStore` to expire under a
|
|
149
|
+
`RecordingRetention`, deletes the provider's copy, keeps a retried callback's recording once, and
|
|
150
|
+
sweeps what expired.
|
|
151
|
+
- **Messages, as their own operation family.** `sendMessage()` (SMS and MMS) and
|
|
152
|
+
`parseMessageWebhook()` on both, running through the client's lifecycle as `messaging` operations.
|
|
153
|
+
- `NexusAI.telephony`, the client's telephony manager; `telnyxRealtimeAudioOptions()`;
|
|
154
|
+
`TelephonyWebhookValidationRequest.publicKey`.
|
|
155
|
+
- The weekly live workflow runs a Gemini Live session that calls a tool, OpenAI's SIP call control,
|
|
156
|
+
and the phone numbers of Twilio and Telnyx, where the repository's secrets are set.
|
|
157
|
+
|
|
158
|
+
### Fixed
|
|
159
|
+
|
|
160
|
+
- A realtime session's `whenIdle()` resolved while a tool it ran on its own was still running, so
|
|
161
|
+
the tool's result could be missing from the record, and from the model, after it. It now waits for
|
|
162
|
+
running tools and their results, though not for a call waiting on a person's confirmation.
|
|
163
|
+
|
|
164
|
+
### Changed
|
|
165
|
+
|
|
166
|
+
- A sandbox's file methods take a leading `/` as its root, as every workspace does: the process
|
|
167
|
+
sandbox and the container sandbox refused such a path unless it was already under their root.
|
|
168
|
+
`/etc/passwd` is now the workspace's own file, never the container's or the host's.
|
|
169
|
+
- The container sandbox rejects reading a missing file with `ENOENT`, as every workspace does, and
|
|
170
|
+
passes the workspace conformance suite.
|
|
171
|
+
- The file tools describe their files as the workspace's, not the sandbox's.
|
|
172
|
+
|
|
173
|
+
### Proof
|
|
174
|
+
|
|
175
|
+
- `tests/workspaces.test.ts`: every workspace the package ships passes the workspace conformance
|
|
176
|
+
suite: in memory, on disk, in the memory, SQLite, Postgres, and Redis stores, the process sandbox,
|
|
177
|
+
and a composite. A composite's shadowing, read-only mounts, and search across mounts.
|
|
178
|
+
- `tests/phone-agent.test.ts`: one phone agent on Twilio and on Telnyx: answered the same way,
|
|
179
|
+
recorded, streamed through the bridge to a realtime session that hears the caller, answers aloud,
|
|
180
|
+
texts a receipt, and transfers the call; its recording kept in memory, on disk, and in S3, deleted
|
|
181
|
+
at the provider, kept once on a retried callback, there the day before its retention ends and gone
|
|
182
|
+
after the day after's sweep. Without the claim, the expiry, the provider delete, the answer by
|
|
183
|
+
command, the messaging family, or `whenIdle()` waiting for tools, it fails.
|
|
184
|
+
- `tests/telephony-providers.test.ts`: both providers against stand-ins for their APIs, and Telnyx's
|
|
185
|
+
webhooks verified with a real Ed25519 key pair: signed, altered, stale, and unsigned.
|
|
186
|
+
- `tests/deep-agent-workspaces.test.ts`: one deep agent does the same work over every workspace and
|
|
187
|
+
each ends holding the same files; and an agent whose code is in a sandbox, notes in a store, and
|
|
188
|
+
docs read-only finds each where it belongs. Without the composite's shadowing, its read-only
|
|
189
|
+
mounts, the store's directory filter, or the agent taking its files from the workspace, a test
|
|
190
|
+
fails.
|
|
191
|
+
|
|
7
192
|
## [2.10.0] - 2026-10-16
|
|
8
193
|
|
|
9
194
|
Durable conversations, and a second realtime provider.
|
|
@@ -2694,7 +2879,9 @@ shared policy vocabulary is in place for a future release that revisits this.
|
|
|
2694
2879
|
- URL fetching rejects unsafe private-network targets and limits response reads.
|
|
2695
2880
|
- CLI and audit findings no longer disclose detected secret values.
|
|
2696
2881
|
|
|
2697
|
-
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.
|
|
2882
|
+
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.12.0...HEAD
|
|
2883
|
+
[2.12.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.11.0...v2.12.0
|
|
2884
|
+
[2.11.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.10.0...v2.11.0
|
|
2698
2885
|
[2.10.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.9.0...v2.10.0
|
|
2699
2886
|
[2.9.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.8.0...v2.9.0
|
|
2700
2887
|
[2.8.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.7.0...v2.8.0
|
package/README.md
CHANGED
|
@@ -299,14 +299,22 @@ Shipped so far:
|
|
|
299
299
|
sessions taken over by another worker when theirs dies, each proven killed at every write; realtime
|
|
300
300
|
on the client's lifecycle; Gemini Live behind the same session, with video and images; phone calls
|
|
301
301
|
answered over SIP; helpers on another server whose runs outlive their worker; and realtime over
|
|
302
|
-
real WebRTC in a browser, with published budgets for turn latency and session load
|
|
302
|
+
real WebRTC in a browser, with published budgets for turn latency and session load;
|
|
303
|
+
- 2.11 workspaces, and telephony in full: an agent's files apart from where its code runs, in memory,
|
|
304
|
+
on disk, in any long-term store, read-only, or a composite of several; and a second phone provider,
|
|
305
|
+
Telnyx, beside Twilio, both answering, transferring, conferencing, recording under a retention
|
|
306
|
+
policy, and sending and receiving messages;
|
|
307
|
+
- 2.12 observability that loses nothing, and media you can evaluate: spans spooled through a collector
|
|
308
|
+
outage, OTLP over gRPC, deployment evaluation that reads each run's whole trace, evaluators that
|
|
309
|
+
read and judge images, documents, audio, and video, a catalog of every stored asset, and automation
|
|
310
|
+
rules that roll a canary back when a score falls.
|
|
303
311
|
|
|
304
312
|
Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
|
|
305
313
|
[MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
|
|
306
|
-
Next, 2.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
workflow passes against each. The
|
|
314
|
+
Next, 2.13 runs agents across regions, keeps each tenant's requests and data in the region they
|
|
315
|
+
require, and resolves provider keys per tenant from a secret manager. Pinecone, images, the Gemini
|
|
316
|
+
embedding batch tier, media understanding, video, Gemini Live, SIP, and both phone providers graduate
|
|
317
|
+
once the live workflow passes against each. The
|
|
310
318
|
[roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists what comes after, in the order
|
|
311
319
|
applications ask for it.
|
|
312
320
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The file methods of a directory on disk. */
|
|
2
|
+
export interface DirectoryFiles {
|
|
3
|
+
readFile(file: string): Promise<string>;
|
|
4
|
+
writeFile(file: string, content: string): Promise<void>;
|
|
5
|
+
listFiles(directory?: string): Promise<string[]>;
|
|
6
|
+
removeFile(file: string): Promise<void>;
|
|
7
|
+
glob(pattern: string, options?: {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
limit?: number;
|
|
10
|
+
}): Promise<string[]>;
|
|
11
|
+
grep(pattern: string, options?: {
|
|
12
|
+
cwd?: string;
|
|
13
|
+
glob?: string;
|
|
14
|
+
ignoreCase?: boolean;
|
|
15
|
+
limit?: number;
|
|
16
|
+
}): Promise<Array<{
|
|
17
|
+
file: string;
|
|
18
|
+
line: number;
|
|
19
|
+
text: string;
|
|
20
|
+
}>>;
|
|
21
|
+
readBytes(file: string): Promise<Uint8Array>;
|
|
22
|
+
writeBytes(file: string, bytes: Uint8Array): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/** The files under `root`, refusing a path outside it with the error `refuse` makes. */
|
|
25
|
+
export declare function directoryFiles(rootDirectory: string, refuse: (path: string) => Error): DirectoryFiles & {
|
|
26
|
+
ready: Promise<DirectoryHandle>;
|
|
27
|
+
};
|
|
28
|
+
/** What a directory's file methods hold once Node's modules have loaded. */
|
|
29
|
+
export interface DirectoryHandle {
|
|
30
|
+
fs: typeof import('node:fs/promises');
|
|
31
|
+
dirname: typeof import('node:path').dirname;
|
|
32
|
+
join: typeof import('node:path').join;
|
|
33
|
+
relative: typeof import('node:path').relative;
|
|
34
|
+
root: string;
|
|
35
|
+
inside: (file: string) => string;
|
|
36
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { GREP_FILE_BYTES, GREP_TOTAL_BYTES, globMatcher, matchingLines, SKIPPED_DIRECTORIES, WALK_LIMIT, } from './glob.js';
|
|
2
|
+
export function directoryFiles(rootDirectory, refuse) {
|
|
3
|
+
const ready = (async () => {
|
|
4
|
+
const [fs, { dirname, isAbsolute, join, relative, resolve }] = await Promise.all([
|
|
5
|
+
import('node:fs/promises'),
|
|
6
|
+
import('node:path'),
|
|
7
|
+
]);
|
|
8
|
+
const root = resolve(rootDirectory);
|
|
9
|
+
await fs.mkdir(root, { recursive: true });
|
|
10
|
+
const outside = (fromRoot) => fromRoot.startsWith('..') || isAbsolute(fromRoot);
|
|
11
|
+
const inside = (file) => {
|
|
12
|
+
let resolved = resolve(root, file);
|
|
13
|
+
if (outside(relative(root, resolved)) && /^[\\/]/.test(file))
|
|
14
|
+
resolved = resolve(root, file.replace(/^[\\/]+/, ''));
|
|
15
|
+
if (outside(relative(root, resolved)))
|
|
16
|
+
throw refuse(file);
|
|
17
|
+
return resolved;
|
|
18
|
+
};
|
|
19
|
+
return { fs, dirname, join, relative, root, inside };
|
|
20
|
+
})();
|
|
21
|
+
ready.catch(() => undefined);
|
|
22
|
+
const walk = async (directory) => {
|
|
23
|
+
const { fs, join, relative, root, inside } = await ready;
|
|
24
|
+
const start = inside(directory);
|
|
25
|
+
const slash = (path) => path.replace(/\\/g, '/');
|
|
26
|
+
const files = [];
|
|
27
|
+
const queue = [start];
|
|
28
|
+
let walked = 0;
|
|
29
|
+
while (queue.length > 0 && walked < WALK_LIMIT) {
|
|
30
|
+
const current = queue.shift();
|
|
31
|
+
const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => []);
|
|
32
|
+
for (const entry of entries) {
|
|
33
|
+
walked += 1;
|
|
34
|
+
if (walked > WALK_LIMIT)
|
|
35
|
+
break;
|
|
36
|
+
const full = join(current, entry.name);
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
if (!SKIPPED_DIRECTORIES.has(entry.name))
|
|
39
|
+
queue.push(full);
|
|
40
|
+
}
|
|
41
|
+
else if (entry.isFile()) {
|
|
42
|
+
files.push({ full, fromRoot: slash(relative(root, full)), fromStart: slash(relative(start, full)) });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return files.sort((a, b) => (a.fromRoot < b.fromRoot ? -1 : a.fromRoot > b.fromRoot ? 1 : 0));
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
ready,
|
|
50
|
+
async readFile(file) {
|
|
51
|
+
const { fs, inside } = await ready;
|
|
52
|
+
return fs.readFile(inside(file), 'utf8');
|
|
53
|
+
},
|
|
54
|
+
async writeFile(file, content) {
|
|
55
|
+
const { fs, dirname, inside } = await ready;
|
|
56
|
+
const target = inside(file);
|
|
57
|
+
await fs.mkdir(dirname(target), { recursive: true });
|
|
58
|
+
await fs.writeFile(target, content, 'utf8');
|
|
59
|
+
},
|
|
60
|
+
async listFiles(directory = '.') {
|
|
61
|
+
const { fs, inside } = await ready;
|
|
62
|
+
return (await fs.readdir(inside(directory))).sort();
|
|
63
|
+
},
|
|
64
|
+
async removeFile(file) {
|
|
65
|
+
const { fs, root, inside } = await ready;
|
|
66
|
+
const target = inside(file);
|
|
67
|
+
if (target === root)
|
|
68
|
+
throw refuse(file);
|
|
69
|
+
await fs.rm(target, { recursive: true, force: true });
|
|
70
|
+
},
|
|
71
|
+
async glob(pattern, options = {}) {
|
|
72
|
+
const matches = globMatcher(pattern);
|
|
73
|
+
return (await walk(options.cwd ?? '.'))
|
|
74
|
+
.filter((file) => matches(file.fromStart))
|
|
75
|
+
.slice(0, options.limit ?? 1_000)
|
|
76
|
+
.map((file) => file.fromRoot);
|
|
77
|
+
},
|
|
78
|
+
async grep(pattern, options = {}) {
|
|
79
|
+
const { fs } = await ready;
|
|
80
|
+
const expression = new RegExp(pattern, options.ignoreCase ? 'i' : '');
|
|
81
|
+
const only = options.glob ? globMatcher(options.glob) : undefined;
|
|
82
|
+
const limit = options.limit ?? 200;
|
|
83
|
+
const found = [];
|
|
84
|
+
let scanned = 0;
|
|
85
|
+
for (const file of await walk(options.cwd ?? '.')) {
|
|
86
|
+
if (only && !only(file.fromStart))
|
|
87
|
+
continue;
|
|
88
|
+
const size = (await fs.stat(file.full).catch(() => undefined))?.size;
|
|
89
|
+
if (size === undefined || size > GREP_FILE_BYTES)
|
|
90
|
+
continue;
|
|
91
|
+
scanned += size;
|
|
92
|
+
if (scanned > GREP_TOTAL_BYTES)
|
|
93
|
+
break;
|
|
94
|
+
const lines = matchingLines(await fs.readFile(file.full), expression, limit - found.length);
|
|
95
|
+
found.push(...lines.map((match) => ({ file: file.fromRoot, ...match })));
|
|
96
|
+
if (found.length >= limit)
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
return found;
|
|
100
|
+
},
|
|
101
|
+
async readBytes(file) {
|
|
102
|
+
const { fs, inside } = await ready;
|
|
103
|
+
return new Uint8Array(await fs.readFile(inside(file)));
|
|
104
|
+
},
|
|
105
|
+
async writeBytes(file, bytes) {
|
|
106
|
+
const { fs, dirname, inside } = await ready;
|
|
107
|
+
const target = inside(file);
|
|
108
|
+
await fs.mkdir(dirname(target), { recursive: true });
|
|
109
|
+
await fs.writeFile(target, bytes);
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentMiddleware } from '../create-agent.js';
|
|
2
|
-
import type {
|
|
3
|
-
/** Where `filesystemContext()` reads and writes: a `Sandbox`, or anything with the same file methods. */
|
|
4
|
-
export type FilesystemContextSource = Pick<
|
|
2
|
+
import type { Workspace } from '../workspace.js';
|
|
3
|
+
/** Where `filesystemContext()` reads and writes: a `Workspace` or a `Sandbox`, or anything with the same file methods. */
|
|
4
|
+
export type FilesystemContextSource = Pick<Workspace, 'readFile'> & Partial<Pick<Workspace, 'writeFile' | 'listFiles'>>;
|
|
5
5
|
/** Where large tool results go instead of the transcript. */
|
|
6
6
|
export interface ContextOffloadOptions {
|
|
7
7
|
/** Results longer than this, in characters, are offloaded. Defaults to 20,000. */
|
|
@@ -12,7 +12,8 @@ export function containerSandbox(options) {
|
|
|
12
12
|
if (unified.includes('\0'))
|
|
13
13
|
throw new SandboxPathError(file);
|
|
14
14
|
const parts = [];
|
|
15
|
-
|
|
15
|
+
const rooted = unified === workdir || unified.startsWith(`${workdir}/`);
|
|
16
|
+
for (const segment of (rooted ? unified : `${workdir}/${unified.replace(/^\/+/, '')}`).split('/')) {
|
|
16
17
|
if (segment === '' || segment === '.')
|
|
17
18
|
continue;
|
|
18
19
|
if (segment !== '..')
|
|
@@ -147,9 +148,10 @@ export function containerSandbox(options) {
|
|
|
147
148
|
};
|
|
148
149
|
const readBytes = async (file) => {
|
|
149
150
|
const result = await shell('cat "$1"', [inside(file)]);
|
|
150
|
-
if (result.code
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
if (result.code === 0)
|
|
152
|
+
return result.stdout;
|
|
153
|
+
const error = failed(`Reading ${file}`, result);
|
|
154
|
+
throw /no such file/i.test(result.stderr) ? Object.assign(error, { code: 'ENOENT', path: file }) : error;
|
|
153
155
|
};
|
|
154
156
|
return {
|
|
155
157
|
name: `container (${binary} ${options.image})`,
|
package/dist/agent/sandbox.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The sandbox contract: where an agent's commands and code run
|
|
2
|
+
* The sandbox contract: where an agent's commands and code run. A sandbox is a `Workspace` that can
|
|
3
|
+
* also run commands, and says how it is isolated, so its files are the files its commands see.
|
|
3
4
|
*
|
|
4
5
|
* The package ships the contract and a conformance suite, not the isolation: a container, a VM, a
|
|
5
6
|
* remote interpreter, or a hosted sandbox fills the `Sandbox` interface. `processSandbox()` is a
|
|
@@ -7,6 +8,7 @@
|
|
|
7
8
|
* `isolation`, and is never a security boundary.
|
|
8
9
|
*/
|
|
9
10
|
import type { ToolDefinition } from '../types/messages.js';
|
|
11
|
+
import { type Workspace, type WorkspaceGlobOptions, type WorkspaceGrepOptions, type WorkspaceMatch, WorkspacePathError } from './workspace.js';
|
|
10
12
|
/** Options for one command. */
|
|
11
13
|
export interface SandboxExecOptions {
|
|
12
14
|
/** The working directory, inside the sandbox. Defaults to its root. */
|
|
@@ -33,33 +35,12 @@ export interface SandboxExecResult {
|
|
|
33
35
|
/** How long it ran, in milliseconds. */
|
|
34
36
|
durationMs: number;
|
|
35
37
|
}
|
|
36
|
-
/** Options for `Sandbox.glob()
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
/** Options for `Sandbox.grep()`. */
|
|
44
|
-
export interface SandboxGrepOptions {
|
|
45
|
-
/** The directory to search, inside the sandbox. Defaults to its root. */
|
|
46
|
-
cwd?: string;
|
|
47
|
-
/** Searches only the files whose paths, relative to `cwd`, match this glob. */
|
|
48
|
-
glob?: string;
|
|
49
|
-
/** Matches without regard to case. */
|
|
50
|
-
ignoreCase?: boolean;
|
|
51
|
-
/** The most matching lines returned. Defaults to 200. */
|
|
52
|
-
limit?: number;
|
|
53
|
-
}
|
|
54
|
-
/** One line `Sandbox.grep()` matched. */
|
|
55
|
-
export interface SandboxMatch {
|
|
56
|
-
/** The file, relative to the sandbox's root. */
|
|
57
|
-
file: string;
|
|
58
|
-
/** The line's number, counting from 1. */
|
|
59
|
-
line: number;
|
|
60
|
-
/** The line, cut to 300 characters. */
|
|
61
|
-
text: string;
|
|
62
|
-
}
|
|
38
|
+
/** Options for `Sandbox.glob()`: a workspace's. */
|
|
39
|
+
export type SandboxGlobOptions = WorkspaceGlobOptions;
|
|
40
|
+
/** Options for `Sandbox.grep()`: a workspace's. */
|
|
41
|
+
export type SandboxGrepOptions = WorkspaceGrepOptions;
|
|
42
|
+
/** One line `Sandbox.grep()` matched: a workspace's match. */
|
|
43
|
+
export type SandboxMatch = WorkspaceMatch;
|
|
63
44
|
/** What a sandbox claims to isolate. The conformance suite checks the claims it can observe. */
|
|
64
45
|
export interface SandboxIsolation {
|
|
65
46
|
/** Commands cannot read or write the host's files. */
|
|
@@ -69,42 +50,20 @@ export interface SandboxIsolation {
|
|
|
69
50
|
/** Commands cannot see or signal the host's processes. */
|
|
70
51
|
processes: boolean;
|
|
71
52
|
}
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Where an agent's commands and code run: a workspace that can also run commands, and says what it
|
|
55
|
+
* isolates. The application provides one.
|
|
56
|
+
*/
|
|
57
|
+
export interface Sandbox extends Workspace {
|
|
76
58
|
/** What it isolates. */
|
|
77
59
|
readonly isolation: SandboxIsolation;
|
|
78
|
-
/** Runs a command line in the sandbox. */
|
|
60
|
+
/** Runs a command line in the sandbox, where its files are. */
|
|
79
61
|
exec(command: string, options?: SandboxExecOptions): Promise<SandboxExecResult>;
|
|
80
|
-
/** Reads a file, by a path inside the sandbox. */
|
|
81
|
-
readFile(file: string): Promise<string>;
|
|
82
|
-
/** Writes a file, creating its directory. */
|
|
83
|
-
writeFile(file: string, content: string): Promise<void>;
|
|
84
|
-
/** Lists a directory's entries. */
|
|
85
|
-
listFiles?(directory?: string): Promise<string[]>;
|
|
86
|
-
/** Removes a file or directory. */
|
|
87
|
-
removeFile?(file: string): Promise<void>;
|
|
88
|
-
/**
|
|
89
|
-
* The files under a directory whose paths match a glob, such as `src/**\/*.ts`, sorted. The glob
|
|
90
|
-
* matches paths relative to `cwd`; the paths returned are relative to the root, as `readFile`
|
|
91
|
-
* takes them. Returns at most `limit`.
|
|
92
|
-
*/
|
|
93
|
-
glob?(pattern: string, options?: SandboxGlobOptions): Promise<string[]>;
|
|
94
|
-
/**
|
|
95
|
-
* The lines matching a regular expression in the files under a directory, at most `limit`. Large
|
|
96
|
-
* files and binary files are skipped.
|
|
97
|
-
*/
|
|
98
|
-
grep?(pattern: string, options?: SandboxGrepOptions): Promise<SandboxMatch[]>;
|
|
99
|
-
/** Reads a file's bytes, for an image, audio, or a document. */
|
|
100
|
-
readBytes?(file: string): Promise<Uint8Array>;
|
|
101
62
|
/** Releases the sandbox. */
|
|
102
63
|
dispose?(): Promise<void>;
|
|
103
64
|
}
|
|
104
|
-
/** Raised when a path would leave the sandbox
|
|
105
|
-
export declare class SandboxPathError extends
|
|
106
|
-
/** The path that was refused. */
|
|
107
|
-
readonly path: string;
|
|
65
|
+
/** Raised when a path would leave the sandbox: a `WorkspacePathError` whose code is `SANDBOX_PATH`. */
|
|
66
|
+
export declare class SandboxPathError extends WorkspacePathError {
|
|
108
67
|
/** Always `SANDBOX_PATH`. */
|
|
109
68
|
readonly code = "SANDBOX_PATH";
|
|
110
69
|
constructor(
|