nanocodex 0.6.5 → 0.6.7
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 +311 -6
- package/THIRD-PARTY-LICENSES +23 -0
- package/browser/Agent.d.mts +5 -4
- package/browser/Agent.mjs +4 -0
- package/browser/Claude.d.mts +4 -0
- package/browser/Claude.mjs +11 -0
- package/browser/InlineAgent.mjs +19 -1
- package/browser/Voice.mjs +11 -4
- package/browser/VoiceSession.mjs +20 -6
- package/browser/host.d.mts +54 -1
- package/browser/host.mjs +65 -16
- package/browser/hostManagedWebSocket.mjs +11 -2
- package/browser/index.d.mts +3 -10
- package/browser/index.mjs +2 -5
- package/browser/socket-observations.mjs +203 -0
- package/cloud/Decorator.d.mts +2 -2
- package/cloud/Decorator.mjs +1 -0
- package/cloud/Dialog.d.mts +13 -0
- package/cloud/Dialog.mjs +52 -6
- package/cloud/Transport.d.mts +0 -11
- package/cloud/Transport.mjs +1 -386
- package/cloud/actions/account.d.mts +12 -0
- package/cloud/actions/account.mjs +11 -0
- package/cloud/actions/agent.mjs +3 -0
- package/cloud/actions/connection.d.mts +3 -1
- package/cloud/actions/connection.mjs +19 -4
- package/cloud/index.d.mts +1 -1
- package/cloud/index.mjs +1 -1
- package/cloud/internal.mjs +19 -4
- package/cloud/mercator.mjs +1 -1
- package/cloud/mock.d.mts +17 -0
- package/cloud/mock.mjs +392 -0
- package/cloud/types.d.mts +4 -1
- package/cloudflare/Agent.d.mts +6 -0
- package/cloudflare/Agent.mjs +190 -10
- package/cloudflare/agent-settings.d.mts +1 -1
- package/cloudflare/agent-settings.mjs +12 -3
- package/cloudflare/egress.mjs +9 -6
- package/cloudflare/gateway-binding-responses.mjs +3 -3
- package/cloudflare/gateway-responses.d.mts +1 -0
- package/cloudflare/gateway-responses.mjs +7 -7
- package/cloudflare/managed-auth.d.mts +4 -4
- package/cloudflare/managed-auth.mjs +20 -2
- package/cloudflare/managed-live.d.mts +6 -0
- package/cloudflare/managed-live.mjs +56 -1
- package/cloudflare/tracing.d.mts +16 -0
- package/cloudflare/tracing.mjs +47 -0
- package/cloudflare/workers-ai-responses.d.mts +3 -2
- package/cloudflare/workers-ai-responses.mjs +83 -8
- package/host/Agent.d.mts +6 -2
- package/host/Claude.d.mts +3 -0
- package/host/Claude.mjs +2 -0
- package/host/index.d.mts +6 -10
- package/host/index.mjs +2 -5
- package/index.d.mts +11 -10
- package/index.mjs +0 -5
- package/internal.mjs +33 -3
- package/managed/Agent.d.mts +10 -3
- package/managed/Agent.mjs +60 -10
- package/managed/ManagedError.d.mts +3 -0
- package/managed/Voice.mjs +3 -1
- package/managed/internal.mjs +2 -1
- package/node/Agent.d.mts +6 -2
- package/node/Agent.mjs +14 -1
- package/node/Claude.d.mts +4 -0
- package/node/Claude.mjs +24 -0
- package/node/host.mjs +18 -9
- package/node/index.d.mts +6 -10
- package/node/index.mjs +2 -5
- package/package.json +45 -9
- package/pkg-node/nanocodex.d.ts +123 -2
- package/pkg-node/nanocodex.js +879 -89
- package/pkg-node/nanocodex_bg.wasm.d.ts +49 -3
- package/pkg-web/.nanocodex-bindgen-stamp +4 -4
- package/pkg-web/nanocodex-build.json +1 -1
- package/pkg-web/nanocodex.d.ts +172 -5
- package/pkg-web/nanocodex.js +872 -88
- package/pkg-web/nanocodex_bg.js +872 -88
- package/pkg-web/nanocodex_bg.wasm +0 -0
- package/pkg-web/nanocodex_bg.wasm.d.ts +49 -3
- package/pkg-web/nanocodex_worker.js +1 -1
- package/runtime/claude-host.mjs +308 -0
- package/runtime/claude-subscription.mjs +79 -0
- package/runtime/claude.d.mts +112 -0
- package/runtime/claude.mjs +221 -0
- package/runtime/code-effect-identity.mjs +123 -0
- package/runtime/harnesses.mjs +51 -0
- package/runtime/mcp-payment.mjs +8 -0
- package/runtime/mcp-runtime.d.mts +10 -0
- package/runtime/mcp-runtime.mjs +242 -55
- package/runtime/mercator.mjs +2 -0
- package/runtime/quickjs-evaluator.mjs +23 -11
- package/runtime/response-controls.mjs +16 -10
- package/runtime/responses-http.mjs +4 -2
- package/runtime/retry-after.d.mts +10 -0
- package/runtime/retry-after.mjs +86 -0
- package/runtime/steer-receipt.mjs +8 -1
- package/runtime/subagent-routing.d.mts +4 -2
- package/runtime/subagent-routing.mjs +9 -3
- package/runtime/subagents.d.mts +8 -3
- package/services/account.d.mts +76 -0
- package/services/account.mjs +151 -0
- package/services/hosted.mjs +72 -0
- package/services/index.d.mts +95 -0
- package/services/index.mjs +192 -0
- package/services/scope.mjs +36 -0
- package/{runtime/tempo-provider.d.mts → tempo/index.d.mts} +20 -1
- package/{runtime/tempo-provider.mjs → tempo/index.mjs} +39 -3
- package/tools/browser/browserShell.mjs +18 -8
- package/tools/browser/python.worker.mjs +17 -1
- package/tools/browser/threadGit.mjs +12 -2
- package/tools/environment.d.mts +17 -0
- package/tools/environment.mjs +24 -1
- package/tools/index.d.mts +6 -3
- package/types.d.mts +107 -6
- package/worker/Claude.d.mts +4 -0
- package/worker/Claude.mjs +11 -0
- package/worker/ClaudeSubscription.d.mts +24 -0
- package/worker/ClaudeSubscription.mjs +9 -0
- package/worker/index.d.mts +9 -0
- package/worker/index.mjs +4 -0
package/README.md
CHANGED
|
@@ -80,6 +80,15 @@ Worker, Durable Object, or application proxy that owns rotating credentials.
|
|
|
80
80
|
Authentication modes are constructors rather than a union of mutually
|
|
81
81
|
exclusive fields on `Agent.create`.
|
|
82
82
|
|
|
83
|
+
### Explicit Claude runtime
|
|
84
|
+
|
|
85
|
+
`Claude.create` is an additive Messages backend with explicit host-owned auth
|
|
86
|
+
and a Claude-only tool array, using the existing durability store contract.
|
|
87
|
+
It does not silently switch managed providers, install Codex tools, or supply
|
|
88
|
+
a subscription sign-in screen. Managed account connection is documented in the
|
|
89
|
+
[managed Claude guide](../../docs/CLAUDE_MANAGED.md). See the [Claude JavaScript guide](../../docs/CLAUDE_JAVASCRIPT.md)
|
|
90
|
+
for durable reopen, replay, and placement boundaries.
|
|
91
|
+
|
|
83
92
|
### Personal memories and caller context
|
|
84
93
|
|
|
85
94
|
Managed agents can keep user preferences separate from team knowledge:
|
|
@@ -252,6 +261,20 @@ normal browser Agent, an account-owned managed Agent, or a grant-scoped
|
|
|
252
261
|
one-use ticket for that path. The browser binding never receives ChatGPT
|
|
253
262
|
credentials or places its reusable grant bearer in a WebSocket URL.
|
|
254
263
|
|
|
264
|
+
### Content-free Worker tracing
|
|
265
|
+
|
|
266
|
+
`nanocodex/cloudflare/tracing` wraps native `tracing.enterSpan(name, callback)`
|
|
267
|
+
with sanitized exception events. Use static operation names: they become
|
|
268
|
+
exception codes on failure. The original error is rethrown; its message and
|
|
269
|
+
stack are never passed to `recordException`.
|
|
270
|
+
|
|
271
|
+
`setSpanAttributes(span, attributes)` sets bulk metadata, while
|
|
272
|
+
`annotateActiveSpan(attributes)` annotates the current invocation or active
|
|
273
|
+
span without changing nesting. `recordSpanException(span, code)` handles
|
|
274
|
+
failures represented by results rather than thrown errors. Supply only static
|
|
275
|
+
codes and content-free attributes. On older workerd versions, these retain
|
|
276
|
+
individual attributes and `error.type` without requiring the September APIs.
|
|
277
|
+
|
|
255
278
|
### Durable Cloudflare Agent
|
|
256
279
|
|
|
257
280
|
`nanocodex/cloudflare` is the standard Durable Object consumer. It keeps the
|
|
@@ -303,10 +326,17 @@ membership, room, quota, or application routing policy. Event frames are
|
|
|
303
326
|
`1013`, then continues by reconnecting with that pause cursor as
|
|
304
327
|
`?cursor=<decimal>`.
|
|
305
328
|
|
|
329
|
+
`toolMode: "code-only"` exposes only `exec` and `wait` to the model. Workspace,
|
|
330
|
+
application, discovery (`tools.tool_search`) and local subagent tools are callable
|
|
331
|
+
inside Code Mode. Discovery returns tool information as cell output; it does not
|
|
332
|
+
add direct schemas. Newly discovered tools are available in the next cell. The
|
|
333
|
+
existing `"code"` mode retains direct workspace/subagent controls, and `"direct"`
|
|
334
|
+
continues to expose function tools without requiring an evaluator.
|
|
335
|
+
|
|
306
336
|
Cloudflare Agents default to direct tool mode because Workers prohibit dynamic
|
|
307
337
|
`eval`/`new Function`. Caller-defined tools therefore work without a code
|
|
308
|
-
evaluator. Select `toolMode: "code"` only when also
|
|
309
|
-
|
|
338
|
+
evaluator. Select `toolMode: "code"` or `toolMode: "code-only"` only when also
|
|
339
|
+
supplying an evaluator explicitly compatible with the deployed Worker runtime. Runtime-owned
|
|
310
340
|
Subagents are installed by default, including on a durable root. All children are
|
|
311
341
|
ephemeral: their identities, topology, conversations, results, and routing exist
|
|
312
342
|
only for the lifetime of the root runtime. Root shutdown or restart discards the
|
|
@@ -588,7 +618,7 @@ shared registry and installs fresh tools for every root, spawn, and fork:
|
|
|
588
618
|
```rust,ignore
|
|
589
619
|
let (registry, control, updates) = nanocodex_subagents::channel(max_concurrency);
|
|
590
620
|
let tools = Tools::builder().without_defaults().build()?;
|
|
591
|
-
let tools =
|
|
621
|
+
let tools = nanocodex_oai_tools::embedded::bind_host(tools, javascript_host);
|
|
592
622
|
let (agent, events) = Nanocodex::builder(openai)
|
|
593
623
|
.tools_factory(move |handle| {
|
|
594
624
|
nanocodex_subagents::install_tools(tools.clone(), handle, registry.clone())
|
|
@@ -655,8 +685,17 @@ API key. Pass an MPP session with a `ws(endpoint)` method; an `mppx` Tempo
|
|
|
655
685
|
session manager has this shape. Nanocodex defaults the socket to
|
|
656
686
|
`wss://openai.mpp.tempo.xyz/v1/responses` when `mpp` is present.
|
|
657
687
|
|
|
688
|
+
Tempo/MPP helpers live in `nanocodex/tempo`, whose optional peer dependencies
|
|
689
|
+
`mppx` and `viem` are needed only when you import it; the core entry points
|
|
690
|
+
never reference them.
|
|
691
|
+
|
|
692
|
+
```sh
|
|
693
|
+
npm install nanocodex mppx viem
|
|
694
|
+
```
|
|
695
|
+
|
|
658
696
|
```js
|
|
659
|
-
import { Agent,
|
|
697
|
+
import { Agent, Transport } from "nanocodex/node";
|
|
698
|
+
import { createTempoProviderFromAccounts } from "nanocodex/tempo";
|
|
660
699
|
import { Expiry } from "accounts";
|
|
661
700
|
import { Provider } from "accounts/cli";
|
|
662
701
|
import { parseUnits } from "viem";
|
|
@@ -759,6 +798,22 @@ the relay preserves MPP challenges, credentials, and receipts but never signs.
|
|
|
759
798
|
Passing a generic `MppSession`, an OpenAI key, or ChatGPT host auth does not
|
|
760
799
|
initialize Mercator. Pass `mcp: false` to opt out explicitly.
|
|
761
800
|
|
|
801
|
+
Trusted hosts can set a server's `privateResult` policy to intercept raw MCP
|
|
802
|
+
results before model output, Code Mode values, and tool-result tracing.
|
|
803
|
+
`beforeCall({ name, arguments }, context)` runs after payment context validation
|
|
804
|
+
and before remote dispatch. It may reject the call or return `{ privateContext }`. An own
|
|
805
|
+
`result` property instead supplies an already-safe receipt and bypasses both
|
|
806
|
+
remote dispatch and `transformResult`, allowing durable policies to replay a
|
|
807
|
+
receipt without repeating an effect.
|
|
808
|
+
`transformResult({ name, arguments, result, privateContext }, context)` receives
|
|
809
|
+
that invocation's `privateContext` and must return only safe MCP content. State stays local
|
|
810
|
+
to each call, including concurrent calls; it is `undefined` without `beforeCall`.
|
|
811
|
+
Both hooks run within the tool deadline. Failed private requests return a fixed
|
|
812
|
+
failure receipt without the original error or cause. Discovery failures also use
|
|
813
|
+
a fixed message. Callbacks and caller-owned clients remain trusted: they must
|
|
814
|
+
not independently log private payloads or return private state. No notification
|
|
815
|
+
consumer is configured by this policy.
|
|
816
|
+
|
|
762
817
|
Remote Streamable HTTP MCP servers are configured directly on the agent. The
|
|
763
818
|
JavaScript binding uses the official MCP SDK transport, keeps remote tools
|
|
764
819
|
deferred, and mirrors native Nanocodex exposure: the initial Responses request
|
|
@@ -775,6 +830,9 @@ supports Tempo charge and session challenges, so paid services composed behind
|
|
|
775
830
|
Mercator use the same signer and spending policy as the model:
|
|
776
831
|
|
|
777
832
|
```js
|
|
833
|
+
import { tempo } from "mppx/client";
|
|
834
|
+
import { createTempoProvider } from "nanocodex/tempo";
|
|
835
|
+
|
|
778
836
|
const mcpMethod = tempo({
|
|
779
837
|
account,
|
|
780
838
|
channelStore,
|
|
@@ -795,6 +853,17 @@ const agent = await Agent.create({
|
|
|
795
853
|
|
|
796
854
|
Explicit `mcp` entries are merged over the Tempo defaults, so an application
|
|
797
855
|
can replace `mercator` or add other servers without rebuilding the provider.
|
|
856
|
+
A paid server configured directly must wrap its options with `mcpPayment()` from
|
|
857
|
+
`nanocodex/tempo`, which loads `mppx/mcp/client` on first connection; a plain
|
|
858
|
+
`payment` object is rejected with a `TypeError`:
|
|
859
|
+
|
|
860
|
+
```js
|
|
861
|
+
import { mcpPayment } from "nanocodex/tempo";
|
|
862
|
+
|
|
863
|
+
const mcp = {
|
|
864
|
+
paid: { url: "https://paid.example/mcp", payment: mcpPayment({ methods: [mcpMethod] }) },
|
|
865
|
+
};
|
|
866
|
+
```
|
|
798
867
|
|
|
799
868
|
Each server also accepts `headers`, `fetch`, allow/deny tool lists, a timeout,
|
|
800
869
|
or an already initialized MCP SDK-compatible `client`. Nanocodex closes clients
|
|
@@ -810,6 +879,80 @@ session and are invalidated when the host shuts down; a persisted `wait` never
|
|
|
810
879
|
restarts missing work. Embedded cells retain ownership of all nested tool calls
|
|
811
880
|
until they finish or are cancelled.
|
|
812
881
|
|
|
882
|
+
Opt into observer-only instant steering with `instantToolSteering: true` in
|
|
883
|
+
Node/browser or Cloudflare Agent creation. Managed creation uses
|
|
884
|
+
`configuration: { instant_tool_steering: true }`. The default is false. Accepted
|
|
885
|
+
steering yields a foreground `exec`/`wait` with its output and live cell ID,
|
|
886
|
+
without cancelling evaluation or nested effects. Later `wait` resumes that
|
|
887
|
+
same cell. Model streams are not interrupted by this option; see
|
|
888
|
+
[the runtime boundaries](../../docs/codex-code-mode.md#observer-only-instant-steering).
|
|
889
|
+
|
|
890
|
+
Owned `nanocodex/node` and `nanocodex/host` agents may opt into
|
|
891
|
+
`codeEffectJournal` alongside their durability store. This trusted host adapter
|
|
892
|
+
must durably acknowledge `begin(context)` before dispatch, validate the original
|
|
893
|
+
session/call identity plus source/tool/input fingerprint, and fence stale owners.
|
|
894
|
+
Journal keys must include
|
|
895
|
+
`[sessionId, operationId ?? "", modelCallIndex ?? 0, parentCallId, callId]`.
|
|
896
|
+
Owned SDK hosts synchronously observe original Rust request/idempotency identity
|
|
897
|
+
and model-call ordinal before dispatch; missing canonical metadata interrupts
|
|
898
|
+
rather than falling back to a projected turn/run ID. Provider call IDs may repeat
|
|
899
|
+
across model responses within one operation and across operations. Use an explicit
|
|
900
|
+
prompt operation `id` with a durable owned journal. Deliberately non-durable
|
|
901
|
+
prompts (including ephemeral child tasks) emit an explicit null Rust operation
|
|
902
|
+
ID; the host scopes those invocations to the unique trusted accepted input item.
|
|
903
|
+
That scope does not promise cold replay of a non-durable task. Absent or malformed
|
|
904
|
+
metadata still interrupts before effects. Generic explicit runtimes without
|
|
905
|
+
an identity resolver retain their existing session/parent identity semantics.
|
|
906
|
+
`turnId` is diagnostic metadata, not the effect replay key.
|
|
907
|
+
Child event forwarding may race the host invocation. The owned SDK rendezvous
|
|
908
|
+
with the exact accepted session/turn and fresh tool-call metadata before journal
|
|
909
|
+
admission (one-second deadline, at most 128 waiters, abort/release/dispose cleanup).
|
|
910
|
+
It never derives effect authority from a parent correlation envelope or reuses
|
|
911
|
+
consumed model-call metadata. Parallel nested calls share one identity promise;
|
|
912
|
+
missing or conflicting metadata fails closed without effect dispatch.
|
|
913
|
+
This covers direct application tools (`toolMode: "direct"`) as well as tools
|
|
914
|
+
invoked by Code Mode. Direct contexts use `parentCallId = callId` and
|
|
915
|
+
`source = "host-tool:" + name`; nested contexts use the exact guest source
|
|
916
|
+
and a stable cell/ordinal.
|
|
917
|
+
Adapters can additionally implement `beginCell(context)` and
|
|
918
|
+
`commitStore(context, writes)` together to persist Code Mode `store()` state.
|
|
919
|
+
Before evaluation, `beginCell` durably pins the cell's immutable starting entries;
|
|
920
|
+
recovery returns those same entries even when later cells changed session state.
|
|
921
|
+
The context has `name: "code-cell"`, `callId: parentCallId`, and `input: null`.
|
|
922
|
+
`commitStore` atomically merges only the cell's writes, once per original cell
|
|
923
|
+
identity. A completed-cell replay must not overwrite newer committed writes.
|
|
924
|
+
Completed failed scripts also commit their writes; interrupted/aborted cells do
|
|
925
|
+
not. Session stores remain isolated and entry snapshots are bounded to 8 MiB and
|
|
926
|
+
32,768 nodes. Hosts must fail closed when prior effects exist but their original
|
|
927
|
+
starting snapshot is missing, corrupt, or cannot be proved during an upgrade.
|
|
928
|
+
Without these optional methods, stores remain local to a runtime instance.
|
|
929
|
+
|
|
930
|
+
Deterministic adapter conflicts and corruption should throw an error with
|
|
931
|
+
`code: "CODE_EFFECT_UNKNOWN"`. These errors and invalid replay receipts settle
|
|
932
|
+
as failed unknown outcomes, without admitting further effects. Storage/transport
|
|
933
|
+
exceptions remain host interruptions so genuinely transient failures can recover.
|
|
934
|
+
|
|
935
|
+
Return `execute` for a new retained intent, `replay` with its exact completed
|
|
936
|
+
receipt, or `unknown` for an intent without a durable outcome. `complete` must
|
|
937
|
+
acknowledge storage before either direct tool output or a nested guest result is
|
|
938
|
+
returned. Direct receipts use `value: null` and `thrown: false` (handler failures
|
|
939
|
+
remain failed encoded tool outputs, not guest throws). Unknown direct intents
|
|
940
|
+
return an explicit failed tool result with `outcome: "unknown"` without dispatch.
|
|
941
|
+
Unknown nested effects fail the recovered cell even if guest code catches the error; they are never silently
|
|
942
|
+
rerun. Reconcile external state using the original operation identity.
|
|
943
|
+
|
|
944
|
+
Receipts must contain plain JSON data and are bounded to an aggregate 8 MiB
|
|
945
|
+
and 32,768 entries before output/value copies. Undefined results and raw undefined
|
|
946
|
+
rejections have explicit markers. Identical output/structured/value payloads use
|
|
947
|
+
receipt-local references (or derived JSON text), so ordinary inline media does
|
|
948
|
+
not consume three copies. An oversized or unretainable post-effect receipt interrupts the host and
|
|
949
|
+
leaves its original intent unknown, rather than granting permission to redispatch. Route larger media through a bounded
|
|
950
|
+
artifact/reference tool instead of embedding it in the replay receipt.
|
|
951
|
+
The journal is opt-in for generic SDK hosts; durability alone does not make
|
|
952
|
+
direct or nested application effects safe to rerun after abrupt owner loss.
|
|
953
|
+
It does not journal arbitrary native JavaScript/evaluator effects, built-in
|
|
954
|
+
provider-native tools, or external operations outside the application-tool router.
|
|
955
|
+
|
|
813
956
|
Custom evaluators receive `audio`, `notify`, `yield_control`, `setTimeout`, and
|
|
814
957
|
`clearTimeout` alongside the existing globals in `CodeEvaluatorEnvironment`.
|
|
815
958
|
Forward those helpers into the guest environment to preserve the model-visible
|
|
@@ -824,7 +967,7 @@ keeps deferred MCP plus Code Mode functional in Cloudflare Workers:
|
|
|
824
967
|
|
|
825
968
|
```js
|
|
826
969
|
import asyncVariant from "@jitl/quickjs-wasmfile-release-asyncify";
|
|
827
|
-
import { Agent, createQuickJsEvaluator,
|
|
970
|
+
import { Agent, createQuickJsEvaluator, Transport } from "nanocodex/host";
|
|
828
971
|
import { newQuickJSAsyncWASMModuleFromVariant } from "quickjs-emscripten-core";
|
|
829
972
|
|
|
830
973
|
const quickJs = await newQuickJSAsyncWASMModuleFromVariant(asyncVariant);
|
|
@@ -1167,7 +1310,7 @@ package manager or build step:
|
|
|
1167
1310
|
|
|
1168
1311
|
```html
|
|
1169
1312
|
<script type="module">
|
|
1170
|
-
import { Agent, Transport } from "https://cdn.jsdelivr.net/npm/nanocodex@0.6.
|
|
1313
|
+
import { Agent, Transport } from "https://cdn.jsdelivr.net/npm/nanocodex@0.6.7/host/index.mjs";
|
|
1171
1314
|
const agent = await Agent.create({
|
|
1172
1315
|
transport: Transport.hostManaged({
|
|
1173
1316
|
websocketUrl: "/api/responses",
|
|
@@ -1347,3 +1490,165 @@ against the Connect Worker with fixture provider responses, covering reads,
|
|
|
1347
1490
|
writes, pagination, rate limits, and revoked grants. Worker tests cover routing
|
|
1348
1491
|
and service denial for all 13 API connectors. SDKs that hardcode their API host need a custom transport instead of only
|
|
1349
1492
|
a base URL setting.
|
|
1493
|
+
|
|
1494
|
+
|
|
1495
|
+
## Connect dialog appearance
|
|
1496
|
+
|
|
1497
|
+
Pass visual tokens when creating an embedded or popup Connect dialog:
|
|
1498
|
+
|
|
1499
|
+
```js
|
|
1500
|
+
import { Client, Dialog } from "nanocodex/connect";
|
|
1501
|
+
|
|
1502
|
+
const appearance = {
|
|
1503
|
+
theme: "system",
|
|
1504
|
+
accentColor: "#635bff",
|
|
1505
|
+
fontFamily: '"Open Sans", system-ui, sans-serif',
|
|
1506
|
+
borderRadius: 12,
|
|
1507
|
+
};
|
|
1508
|
+
const client = Client.create({
|
|
1509
|
+
appId: "your-app-id",
|
|
1510
|
+
dialog: Dialog.iframe({ appearance }),
|
|
1511
|
+
});
|
|
1512
|
+
// Dialog.popup({ appearance }) accepts the same options for account authorization.
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
All fields are optional. `theme` accepts `light`, `dark`, or `system`;
|
|
1516
|
+
`accentColor` requires six hex digits; `fontFamily` accepts an installed or
|
|
1517
|
+
self-hosted font list of at most 160 characters; `borderRadius` is a number from
|
|
1518
|
+
0 to 24 pixels. Arbitrary CSS, CSS functions, and unknown options are rejected.
|
|
1519
|
+
The SDK snapshots the configuration when the dialog is created and carries it
|
|
1520
|
+
in the `nanocodex_appearance` URL parameter to wallet and funding iframes or the
|
|
1521
|
+
account popup. The hosted dialog limits the JSON value to 1,024 characters and
|
|
1522
|
+
uses native defaults for malformed values. Omit `appearance` for native defaults.
|
|
1523
|
+
|
|
1524
|
+
### Standalone account services
|
|
1525
|
+
|
|
1526
|
+
`nanocodex/services` accesses Vault and dedicated SMS numbers without an agent,
|
|
1527
|
+
thread, model, or WASM runtime. Keep direct account API keys on your server:
|
|
1528
|
+
|
|
1529
|
+
```js
|
|
1530
|
+
import { createServicesClient } from "nanocodex/services";
|
|
1531
|
+
|
|
1532
|
+
const services = createServicesClient({ apiKey: process.env.NANOCODEX_API_KEY });
|
|
1533
|
+
const { vault } = await services.vault.list();
|
|
1534
|
+
const { status, ok } = await services.vault.request({
|
|
1535
|
+
vault_id: vault[0].id,
|
|
1536
|
+
url: "https://example.com/verify",
|
|
1537
|
+
method: "POST",
|
|
1538
|
+
body_encoding: "json",
|
|
1539
|
+
body: '{"code":"{{NANOCODEX_VAULT_TOTP}}"}',
|
|
1540
|
+
});
|
|
1541
|
+
```
|
|
1542
|
+
|
|
1543
|
+
Direct calls use `https://nanocodex.gakonst.workers.dev/v1/services`. Reads require
|
|
1544
|
+
`data:read`, Vault requests require `tools:use`, and phone intents require
|
|
1545
|
+
`data:write` plus `tools:use`. Vault returns metadata and destination HTTP status
|
|
1546
|
+
only. TOTP seeds and generated codes stay in the broker; the saved exact HTTPS
|
|
1547
|
+
origin bounds code injection. There is no code-export method.
|
|
1548
|
+
|
|
1549
|
+
Browser apps use an existing Connect client and exact signed service capabilities:
|
|
1550
|
+
|
|
1551
|
+
```js
|
|
1552
|
+
import { Client } from "nanocodex/connect";
|
|
1553
|
+
import { createServicesClient } from "nanocodex/services";
|
|
1554
|
+
|
|
1555
|
+
const connect = Client.create({ appId: "example-app" });
|
|
1556
|
+
const connection = await connect.connection.connect({
|
|
1557
|
+
capabilities: {
|
|
1558
|
+
services: {
|
|
1559
|
+
vault: { ids: ["selected-vault-id"], origins: ["https://example.com"], request: true },
|
|
1560
|
+
phone: { numberIds: [], read: false, provision: true, release: false },
|
|
1561
|
+
},
|
|
1562
|
+
},
|
|
1563
|
+
});
|
|
1564
|
+
const services = createServicesClient({ connect, grantId: connection.grant.id });
|
|
1565
|
+
```
|
|
1566
|
+
|
|
1567
|
+
Service-only connections default to `services.use` and hosted authorization.
|
|
1568
|
+
They contain no `agentId`, access key, or MPP budget. Scope is bound into the
|
|
1569
|
+
signed approval and checked against the returned grant. Reconnect with the same
|
|
1570
|
+
`capabilities.services` to require that exact scope. Service requests use
|
|
1571
|
+
`/v1/grants/:grantId/services/...` and the Connect session. Number and Vault IDs
|
|
1572
|
+
are explicit; they never expand to the entire account.
|
|
1573
|
+
|
|
1574
|
+
Use `phone.available({ country: "US", area_code: "202" })`, `phone.list()`,
|
|
1575
|
+
`phone.get(id)`, and `phone.messages(id, { limit: 20, cursor })` for reads. SMS
|
|
1576
|
+
messages expire according to the service's retention policy; support for
|
|
1577
|
+
verification messages depends on the sender and carrier.
|
|
1578
|
+
|
|
1579
|
+
Provisioning and release create requests for human review:
|
|
1580
|
+
|
|
1581
|
+
```js
|
|
1582
|
+
import { createHostedRequest, openHostedPopup } from "nanocodex/services";
|
|
1583
|
+
|
|
1584
|
+
// Persist this UUID before sending; keep it when reconciling an uncertain request.
|
|
1585
|
+
const operation_id = crypto.randomUUID();
|
|
1586
|
+
const { request } = await services.phone.provision({
|
|
1587
|
+
operation_id, phone_number: "+12025550101", country: "US",
|
|
1588
|
+
});
|
|
1589
|
+
const approval = createHostedRequest({
|
|
1590
|
+
service: "phone", operationId: request.approval_request_id ?? request.operation_id, appOrigin: location.origin,
|
|
1591
|
+
});
|
|
1592
|
+
// Open from a separate click handler after the request is ready.
|
|
1593
|
+
await openHostedPopup(approval);
|
|
1594
|
+
const result = await services.phone.requests.get(request.operation_id);
|
|
1595
|
+
```
|
|
1596
|
+
|
|
1597
|
+
`phone.release(numberId, { operation_id })` also requires hosted human approval.
|
|
1598
|
+
Poll with the **original** caller UUID, including after a dropped intent response.
|
|
1599
|
+
Connect returns an additional `request.approval_request_id` for the hosted owner
|
|
1600
|
+
approval; use `request.approval_request_id ?? request.operation_id` for its link.
|
|
1601
|
+
Any deliberate identical intent replay must retain the original caller UUID.
|
|
1602
|
+
A newly provisioned number needs a fresh exact-ID Connect approval before inbox
|
|
1603
|
+
access. The SDK never automatically retries a write. `ServiceError` exposes
|
|
1604
|
+
`status`, `code`, and `outcomeUnknown`; an unknown outcome requires reconciliation,
|
|
1605
|
+
not a new operation ID. No SDK method approves purchases or releases.
|
|
1606
|
+
|
|
1607
|
+
TOTP enrollment uses a private hosted form:
|
|
1608
|
+
|
|
1609
|
+
```js
|
|
1610
|
+
const enrollment = createHostedRequest({ appOrigin: location.origin });
|
|
1611
|
+
// Call from a user click. Inputs stay on the account origin.
|
|
1612
|
+
const metadata = await openHostedPopup(enrollment);
|
|
1613
|
+
if (metadata.service === "vault") console.log(metadata.vault_id);
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
The popup helper checks the sender window, exact hosted origin, and a unique
|
|
1617
|
+
state. Only fixed metadata fields reach the caller. `createHostedRequest`
|
|
1618
|
+
also accepts `host` for a trusted HTTPS account deployment and `state` for a
|
|
1619
|
+
caller-managed unique nonce. Secret entry and approvals require a top-level window; embedded frames cannot
|
|
1620
|
+
perform them. React applications can open the same request with
|
|
1621
|
+
`HostedServiceButton` from `nanocodex-react/services`.
|
|
1622
|
+
|
|
1623
|
+
To use an existing Vault item before granting access, open the account picker
|
|
1624
|
+
from a user click. No service client or Connect grant is needed:
|
|
1625
|
+
|
|
1626
|
+
```js
|
|
1627
|
+
const selection = createHostedRequest({
|
|
1628
|
+
service: "vault", action: "select", appOrigin: location.origin,
|
|
1629
|
+
});
|
|
1630
|
+
const selected = await openHostedPopup(selection);
|
|
1631
|
+
if (selected.service === "vault" && selected.action === "select") {
|
|
1632
|
+
// The user sees the recipient origin and explicitly chooses this item.
|
|
1633
|
+
// Only its opaque vault_id, kind, and name are shared.
|
|
1634
|
+
const connection = await connect.connection.connect({
|
|
1635
|
+
capabilities: { services: { vault: {
|
|
1636
|
+
ids: [selected.vault_id], origins: ["https://example.com"], request: true,
|
|
1637
|
+
} } },
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
```
|
|
1641
|
+
|
|
1642
|
+
The picker does not create or expand a grant and does not enroll the item again.
|
|
1643
|
+
The separate Connect approval authorizes the exact selected ID and destination
|
|
1644
|
+
origins. Closing the popup or aborting its `signal` cancels the local wait;
|
|
1645
|
+
closing a phone approval window does not cancel a server-side operation. Poll
|
|
1646
|
+
that operation with its original caller UUID to learn its outcome.
|
|
1647
|
+
|
|
1648
|
+
Account management is available through `createServicesClient({ apiKey }).account`
|
|
1649
|
+
without an agent or WASM. Use `client.links({ connect: 'google', add: 'login' })`
|
|
1650
|
+
for ordinary Connections/Vault URLs, or
|
|
1651
|
+
`client.hosted({ service: 'vault', kind: 'card' })` for a private enrollment link.
|
|
1652
|
+
OAuth, Cloudflare, MCP, model sign-in, Vault/SSH management, and captured-card
|
|
1653
|
+
save/balance methods share the account REST contracts. See the
|
|
1654
|
+
[REST, JavaScript and Rust guide](../../docs/STANDALONE_SERVICES.md#account-management-and-browser-links).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mario Zechner
|
|
4
|
+
Copyright (c) 2025-2026 Can Bölük
|
|
5
|
+
Copyright (c) 2026 Stencil Labs, Inc.
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/browser/Agent.d.mts
CHANGED
|
@@ -22,10 +22,11 @@ type WorkerMcpServer = Readonly<{
|
|
|
22
22
|
}>;
|
|
23
23
|
type WorkerMcpServers = Readonly<Record<string, string | URL | WorkerMcpServer>>;
|
|
24
24
|
type WorkerToolExposureOptions =
|
|
25
|
-
| { mcp?: false | undefined; toolMode?: "code" | "direct" | undefined }
|
|
26
|
-
| { mcp: WorkerMcpServers; toolMode?: "code" | undefined };
|
|
25
|
+
| { mcp?: false | undefined; toolMode?: "code" | "code-only" | "direct" | undefined }
|
|
26
|
+
| { mcp: WorkerMcpServers; toolMode?: "code" | "code-only" | undefined };
|
|
27
27
|
|
|
28
28
|
/** Creates a Rust/WASM Agent in a package-owned browser module Worker. */
|
|
29
|
+
export function create(options: import('../runtime/claude.mjs').Options & { harness: 'claude' }): Promise<import('../runtime/claude.mjs').Agent>;
|
|
29
30
|
export function create(options: create.ManagedOptions): Promise<AgentLifecycle>;
|
|
30
31
|
export function create(options?: create.Options): Promise<create.ReturnType>;
|
|
31
32
|
export declare namespace create {
|
|
@@ -33,7 +34,7 @@ export declare namespace create {
|
|
|
33
34
|
transport: ManagedTransport;
|
|
34
35
|
tools?: Tools | undefined;
|
|
35
36
|
}>;
|
|
36
|
-
type Options = Omit<AgentOptions, "beforeCompaction"> & WorkerToolExposureOptions & {
|
|
37
|
+
type Options = Omit<AgentOptions, "beforeCompaction" | "harness"> & WorkerToolExposureOptions & {
|
|
37
38
|
/** Precompiled browser module; WebAssembly modules are structured-clone-safe. */
|
|
38
39
|
module?: WebAssembly.Module | undefined;
|
|
39
40
|
/** Fixed browser workspace facts, including its AGENTS.md snapshot. */
|
|
@@ -47,7 +48,7 @@ export declare namespace create {
|
|
|
47
48
|
/** Set false to keep this browser session out of the IndexedDB durability store. */
|
|
48
49
|
durability?: false | undefined;
|
|
49
50
|
/** Set false to omit the default OPFS, shell, web, image, plan, and artifact tools. */
|
|
50
|
-
harness?: false | undefined;
|
|
51
|
+
harness?: false | 'codex' | undefined;
|
|
51
52
|
};
|
|
52
53
|
type ReturnType = Agent;
|
|
53
54
|
}
|
package/browser/Agent.mjs
CHANGED
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
|
|
6
6
|
/** Create the browser Agent in its package-owned module Worker. */
|
|
7
7
|
export function create(options = {}) {
|
|
8
|
+
if (options.harness === 'claude') return import('./Claude.mjs').then(({ create }) => create(options));
|
|
9
|
+
if (options.harness !== undefined && options.harness !== false && options.harness !== 'codex') throw new TypeError('unsupported harness family');
|
|
10
|
+
if (options.harnesses !== undefined) return import('./InlineAgent.mjs').then(({ create }) =>
|
|
11
|
+
create(options.harness === false ? { ...options, harness: 'codex' } : options));
|
|
8
12
|
if (managedTransportOptions(options?.transport)) return createManagedAgent(options);
|
|
9
13
|
return import("./WorkerAgent.mjs").then(({ createWorkerAgent }) =>
|
|
10
14
|
createWorkerAgent(options));
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Agent, Options } from '../runtime/claude.mjs';
|
|
2
|
+
export type { Agent, Auth, Options, NativeToolResult, Result, Tool, ToolContent, ToolResult, Turn } from '../runtime/claude.mjs';
|
|
3
|
+
/** Creates the actual Claude Rust/WASM backend with explicit host-owned authentication and tools. */
|
|
4
|
+
export function create(options: Options): Promise<Agent>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createClaude } from '../runtime/claude.mjs';
|
|
2
|
+
import { initializeBrowserEngine } from './engine.mjs';
|
|
3
|
+
|
|
4
|
+
/** Runs inline in this browser isolate; not the Codex Agent module Worker. */
|
|
5
|
+
export function create(options) {
|
|
6
|
+
return createClaude(options, async (module) => {
|
|
7
|
+
const wasm = await import('../pkg-web/nanocodex.js');
|
|
8
|
+
await initializeBrowserEngine({ module });
|
|
9
|
+
return wasm.Nanoclaude;
|
|
10
|
+
}, 'browser');
|
|
11
|
+
}
|
package/browser/InlineAgent.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { prepareHarnesses } from '../runtime/harnesses.mjs';
|
|
2
|
+
import { create as createClaude } from './Claude.mjs';
|
|
1
3
|
import { applyBrowserPatch, Nanocodex } from "../pkg-web/nanocodex.js";
|
|
2
4
|
|
|
3
5
|
import { agentActions } from "../actions/index.mjs";
|
|
@@ -33,6 +35,8 @@ import {
|
|
|
33
35
|
|
|
34
36
|
/** Creates the Rust/WASM Agent in the current Web API host isolate. */
|
|
35
37
|
export async function create(options = {}) {
|
|
38
|
+
if (options.harness === 'claude') return createClaude(options);
|
|
39
|
+
if (options.harness !== undefined && options.harness !== 'codex') throw new TypeError('unsupported harness family');
|
|
36
40
|
if (managedTransportOptions(options?.transport)) return createManagedAgent(options);
|
|
37
41
|
const internalRuntime = options[Symbol.for("nanocodex.browser.internalRuntime")];
|
|
38
42
|
if (internalRuntime !== undefined
|
|
@@ -46,6 +50,7 @@ export async function create(options = {}) {
|
|
|
46
50
|
thinking,
|
|
47
51
|
reasoningMode,
|
|
48
52
|
fastMode,
|
|
53
|
+
instantToolSteering,
|
|
49
54
|
rawApiEvents,
|
|
50
55
|
instructions,
|
|
51
56
|
additionalInstructions,
|
|
@@ -63,6 +68,7 @@ export async function create(options = {}) {
|
|
|
63
68
|
mcp,
|
|
64
69
|
executionEnvironment,
|
|
65
70
|
codeEvaluator,
|
|
71
|
+
codeEffectJournal,
|
|
66
72
|
} = options;
|
|
67
73
|
const toolProviders = internalRuntime?.toolProviders;
|
|
68
74
|
const subagentSessions = internalRuntime?.subagentSessions;
|
|
@@ -109,9 +115,12 @@ export async function create(options = {}) {
|
|
|
109
115
|
throw new TypeError("workspace must match filesystem.root when both are provided");
|
|
110
116
|
}
|
|
111
117
|
const events = createEventChannel();
|
|
118
|
+
// Host lifecycle observers need terminal events even without a public watcher.
|
|
119
|
+
events.subscribe(() => {});
|
|
112
120
|
const tempoMcp = mpp?.[Symbol.for("nanocodex.tempo.mcp")];
|
|
113
121
|
let hostDefinitionId;
|
|
114
122
|
const host = createBrowserHost({
|
|
123
|
+
[Symbol.for("nanocodex.browser.internalRuntime")]: { traceTool: internalRuntime?.traceTool },
|
|
115
124
|
WebSocketImpl,
|
|
116
125
|
createWebSocket,
|
|
117
126
|
createResponse,
|
|
@@ -128,16 +137,19 @@ export async function create(options = {}) {
|
|
|
128
137
|
subagentSessions,
|
|
129
138
|
subagentRouting: internalRuntime?.subagentRouting,
|
|
130
139
|
onSocketTiming: internalRuntime?.onSocketTiming,
|
|
140
|
+
onSocketEvent: internalRuntime?.onSocketEvent,
|
|
131
141
|
toolMode,
|
|
132
142
|
mcp: mcp === false
|
|
133
143
|
? undefined
|
|
134
144
|
: tempoMcp ? { ...tempoMcp, ...mcp } : mcp,
|
|
135
145
|
codeEvaluator,
|
|
146
|
+
codeEffectJournal: internalRuntime?.codeEffectJournal ?? codeEffectJournal,
|
|
136
147
|
applyPatch: applyBrowserPatch,
|
|
137
148
|
websocketPreconnect,
|
|
138
149
|
websocketUrl,
|
|
139
|
-
onDispose: () => releaseDefinitionHost(hostDefinitionId),
|
|
150
|
+
onDispose: () => { releaseDefinitionHost(hostDefinitionId); void harnesses?.close(); },
|
|
140
151
|
});
|
|
152
|
+
let harnesses;
|
|
141
153
|
let durabilityOwner;
|
|
142
154
|
let creationStarted = false;
|
|
143
155
|
hostDefinitionId = registerDefinitionHost(host, cloudflareReservation);
|
|
@@ -157,6 +169,9 @@ export async function create(options = {}) {
|
|
|
157
169
|
durabilityId,
|
|
158
170
|
);
|
|
159
171
|
}
|
|
172
|
+
harnesses = await prepareHarnesses(options.harnesses, events.emit, {
|
|
173
|
+
subagentSessions, subagentRouting: internalRuntime?.subagentRouting,
|
|
174
|
+
});
|
|
160
175
|
activateHost(host);
|
|
161
176
|
await host.ready();
|
|
162
177
|
await initializeBrowserEngine({ module });
|
|
@@ -172,9 +187,11 @@ export async function create(options = {}) {
|
|
|
172
187
|
websocketWarmup,
|
|
173
188
|
stateless,
|
|
174
189
|
subagents: subagentConfig,
|
|
190
|
+
claudeHarness: harnesses?.claude,
|
|
175
191
|
subagentRouting: internalRuntime?.subagentRouting !== undefined,
|
|
176
192
|
hostDefinitionId,
|
|
177
193
|
beforeCompaction: beforeCompaction !== undefined,
|
|
194
|
+
promptCacheKey: internalRuntime?.promptCacheKey,
|
|
178
195
|
...config,
|
|
179
196
|
durabilityHostId: durabilityOwner?.id,
|
|
180
197
|
}));
|
|
@@ -246,6 +263,7 @@ export async function create(options = {}) {
|
|
|
246
263
|
thinking,
|
|
247
264
|
reasoningMode,
|
|
248
265
|
fastMode,
|
|
266
|
+
instantToolSteering,
|
|
249
267
|
rawApiEvents,
|
|
250
268
|
instructions,
|
|
251
269
|
additionalInstructions,
|
package/browser/Voice.mjs
CHANGED
|
@@ -198,10 +198,17 @@ export function create(agent, options = {}) {
|
|
|
198
198
|
// Media readiness updates the UI independently; start still waits for task
|
|
199
199
|
// admission, and a later rejection closes media through the same error path.
|
|
200
200
|
startPromise = next.start().then(publishReady).catch(async (cause) => {
|
|
201
|
-
if (session === next)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
if (session === next) {
|
|
202
|
+
session = undefined;
|
|
203
|
+
if (activeResources.get(agent) === resource) activeResources.delete(agent);
|
|
204
|
+
cleanupWatcher();
|
|
205
|
+
}
|
|
206
|
+
// Keep replacement starts behind failed startup cleanup, just like stop().
|
|
207
|
+
// A stale attempt must never detach a replacement session's event watcher.
|
|
208
|
+
const closing = next.close().catch(() => next.abort());
|
|
209
|
+
if (generation === current) stopPromise = closing;
|
|
210
|
+
await closing;
|
|
211
|
+
if (stopPromise === closing) stopPromise = undefined;
|
|
205
212
|
if (destroyed || generation !== current) return;
|
|
206
213
|
if (attempt === 0 && cause?.code === "peer_connection_timeout") return start(parameters, 1);
|
|
207
214
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|