@intentic/sandbox-contract 1.230.2 → 1.231.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/README.md +5 -0
- package/dist/contracts/agent.contract.d.ts +42 -6
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.d.ts +6 -1
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +293 -0
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +10 -1
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/extensions.contract.d.ts +15 -0
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.d.ts +12 -2
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/history.contract.d.ts +6 -1
- package/dist/contracts/history.contract.d.ts.map +1 -1
- package/dist/contracts/panels.contract.d.ts +1 -0
- package/dist/contracts/panels.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +811 -0
- package/dist/contracts/runner.contract.d.ts.map +1 -0
- package/dist/contracts/runner.contract.js +12 -0
- package/dist/contracts/runner.contract.js.map +1 -0
- package/dist/contracts/system.contract.d.ts +2 -3
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/documents.d.ts +7 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +27 -0
- package/dist/documents.js.map +1 -0
- package/dist/events.d.ts +58 -6
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +12 -1
- package/dist/events.js.map +1 -1
- package/dist/history-state.d.ts.map +1 -1
- package/dist/history-state.js +3 -0
- package/dist/history-state.js.map +1 -1
- package/dist/index.d.ts +380 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/runner-protocol.d.ts +134 -0
- package/dist/runner-protocol.d.ts.map +1 -0
- package/dist/runner-protocol.js +95 -0
- package/dist/runner-protocol.js.map +1 -0
- package/dist/schemas.d.ts +62 -9
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +30 -11
- package/dist/schemas.js.map +1 -1
- package/package.json +4 -4
- package/src/contracts/capabilities.contract.ts +16 -0
- package/src/contracts/runner.contract.ts +33 -0
- package/src/documents.test.ts +66 -0
- package/src/documents.ts +71 -0
- package/src/events.ts +44 -2
- package/src/history-state.ts +7 -0
- package/src/index.ts +4 -0
- package/src/runner-protocol.ts +209 -0
- package/src/schemas.ts +106 -37
package/src/schemas.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { ExtensionManifestSchema } from "@intentic/extension-manifest";
|
|
|
3
3
|
import { RegistryEntrySchema } from "@intentic/registry";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { OutputFieldsSchema } from "./output-fields.js";
|
|
6
|
+
import { AgentPlacementSchema } from "./runner-protocol.js";
|
|
6
7
|
|
|
7
8
|
// All request/response wire schemas for the sandbox daemon. Inputs that carry a `{param}` in their route path
|
|
8
9
|
// (repo / id / name) merge the path param into the same flat object, oRPC fills the path placeholder from the
|
|
@@ -253,6 +254,13 @@ export const AgentTurnSchema = z
|
|
|
253
254
|
.describe(
|
|
254
255
|
"Work in this conversation's own private copy of the repos rather than the shared tree, so several agents can work at once. Needs a conversation id.",
|
|
255
256
|
),
|
|
257
|
+
/* WHERE the conversation executes, decided like `isolated` directly above: the request's choice on the
|
|
258
|
+
* first turn, the registry entry's on every turn after. `runner` implies isolation (the branch is what
|
|
259
|
+
* moves between machines) and needs a conversation id for the same reason `isolated` does. Absent =
|
|
260
|
+
* local. Design: docs/remote-runners-plan.md; refused until runners ship. */
|
|
261
|
+
placement: AgentPlacementSchema.optional().describe(
|
|
262
|
+
"Where this conversation runs: this sandbox (leave it out), or a paired runner by id. Decided on the first turn; later turns follow the conversation.",
|
|
263
|
+
),
|
|
256
264
|
/* Pin a NEW isolated conversation's worktree composition to these repository commits. Daemon-owned:
|
|
257
265
|
* ordinary chats omit it and keep rebasing onto the current workspace; a workflow supplies the one
|
|
258
266
|
* snapshot all of its candidates must share. Repeated iterations carry it too, which suppresses the
|
|
@@ -2778,7 +2786,13 @@ export const SandboxSettingsSchema = z.object({
|
|
|
2778
2786
|
* `<provider>.<type>` ("discord.message.send") with `<provider>.*` as the per-provider wildcard; exact key
|
|
2779
2787
|
* wins. An action with no rule is allowed, the empty default wires no hook at all, so an unconfigured
|
|
2780
2788
|
* workspace pays nothing. "hold" cannot park a running turn (nobody may be there to answer); it refuses the
|
|
2781
|
-
* live call and points the agent at the drafts outbox, which IS the held form of a send.
|
|
2789
|
+
* live call and points the agent at the drafts outbox, which IS the held form of a send.
|
|
2790
|
+
*
|
|
2791
|
+
* The CHILD-AGENT surface reads the same book: `agents.spawn` covers starting, steering and answering
|
|
2792
|
+
* child agents on every provider, `agents.spawn.<provider>` singles one out (the specific key wins), and
|
|
2793
|
+
* the daemon's own taint floor holds a spawn from a turn that has taken in outside content unless the
|
|
2794
|
+
* owner wrote an explicit allow (guard/actions.ts childSpawn). "hold" refuses with the owner named, the
|
|
2795
|
+
* same translation a send gets. */
|
|
2782
2796
|
actionRules: z
|
|
2783
2797
|
.record(z.string(), AdmissionRuleSchema)
|
|
2784
2798
|
.default({})
|
|
@@ -3812,13 +3826,43 @@ export const SnapshotFileDiffQuerySchema = z.object({
|
|
|
3812
3826
|
scope: z.string().min(1).describe("Which part of the workspace the path belongs to."),
|
|
3813
3827
|
path: z.string().min(1).describe("The file, relative to that scope."),
|
|
3814
3828
|
});
|
|
3829
|
+
/* WHAT A FILE TOO BIG TO SEND WHOLE ANSWERS WITH INSTEAD, and why that is not simply "no".
|
|
3830
|
+
*
|
|
3831
|
+
* Both whole sides of a half-megabyte file are a megabyte of JSON per click, so above the cap they are not
|
|
3832
|
+
* sent, which used to be the end of it: the response said "too large" and every review surface printed one
|
|
3833
|
+
* sentence over an empty pane. That is the wrong trade, because the thing a reader wants out of a big file is
|
|
3834
|
+
* almost never the file: it is the handful of lines that MOVED, and those are small however big the file is.
|
|
3835
|
+
*
|
|
3836
|
+
* So the daemon diffs it and sends the CHANGED REGIONS as a unified patch, at the same three lines of context
|
|
3837
|
+
* a collapsed region keeps elsewhere. A 40 KB patch stands in for a 60 MB pair, and the reader gets the actual
|
|
3838
|
+
* review rather than a refusal. `patch` carries the `@@` sections only, the file headers git prints above them
|
|
3839
|
+
* name rev-specs no one can apply anyway.
|
|
3840
|
+
*
|
|
3841
|
+
* An added or deleted file has no counterpart to diff against, so its patch IS the file, one region of pure
|
|
3842
|
+
* +/− lines. That is still the right answer: cut to the budget, it is the head of the file, which is the peek
|
|
3843
|
+
* the reader came for.
|
|
3844
|
+
*
|
|
3845
|
+
* `patch` is absent only when there was nothing to make one from: a change too large even to render as a
|
|
3846
|
+
* patch, or a git that refused. The sizes are still there, so a surface can at least say how big the thing it
|
|
3847
|
+
* is not showing is. */
|
|
3848
|
+
export const PartialFileDiffSchema = z.object({
|
|
3849
|
+
beforeBytes: z.number().int().nonnegative().optional().describe("How big the before side is, in bytes. Absent when the file did not exist yet."),
|
|
3850
|
+
afterBytes: z.number().int().nonnegative().optional().describe("How big the after side is, in bytes. Absent when the file was deleted."),
|
|
3851
|
+
patch: z
|
|
3852
|
+
.string()
|
|
3853
|
+
.optional()
|
|
3854
|
+
.describe("The changed regions as unified-diff hunks (`@@` sections only). Absent when the change was too large to render even as a patch."),
|
|
3855
|
+
more: z.boolean().optional().describe("There were more changed regions than fit; the patch stops at a region boundary."),
|
|
3856
|
+
});
|
|
3857
|
+
export type PartialFileDiff = z.infer<typeof PartialFileDiffSchema>;
|
|
3858
|
+
|
|
3815
3859
|
// Both sides of a file diff, a snapshot vs its parent, or a working tree vs HEAD; an absent side means the
|
|
3816
|
-
// file was added/deleted. Binary
|
|
3860
|
+
// file was added/deleted. Binary content is flagged instead of shipped; oversized content arrives as `partial`.
|
|
3817
3861
|
export const FileDiffSchema = z.object({
|
|
3818
|
-
before: z.string().optional().describe("The whole file as it was. Absent when it did not exist yet."),
|
|
3819
|
-
after: z.string().optional().describe("The whole file as it is now. Absent when it was deleted."),
|
|
3862
|
+
before: z.string().optional().describe("The whole file as it was. Absent when it did not exist yet, or when `partial` is set."),
|
|
3863
|
+
after: z.string().optional().describe("The whole file as it is now. Absent when it was deleted, or when `partial` is set."),
|
|
3820
3864
|
binary: z.boolean().optional().describe("The file is not text, so neither side is sent."),
|
|
3821
|
-
|
|
3865
|
+
partial: PartialFileDiffSchema.optional().describe("Set when the file was too large to send whole: what is sent instead of the two sides."),
|
|
3822
3866
|
});
|
|
3823
3867
|
export type FileDiff = z.infer<typeof FileDiffSchema>;
|
|
3824
3868
|
|
|
@@ -5617,6 +5661,26 @@ export const CapabilityOtpSchema = z.object({
|
|
|
5617
5661
|
.describe("How long it lasts. Its expiring is what makes handing one to an agent safe, since the seed behind it is never revealed."),
|
|
5618
5662
|
});
|
|
5619
5663
|
|
|
5664
|
+
/* POST /capabilities/probe response: did these settings actually reach the thing, asked BEFORE they are saved.
|
|
5665
|
+
*
|
|
5666
|
+
* The answer is a sentence rather than a status code because the reader is standing in front of a form: what
|
|
5667
|
+
* they need is either the service's own confirmation ("Reached GitHub, authenticated as ada") or the exact
|
|
5668
|
+
* refusal ("GitHub answered 401: the token is not valid"), in the place where the box they would fix still is.
|
|
5669
|
+
* That is also the whole point of doing it here: every one of these failures is otherwise discovered after the
|
|
5670
|
+
* add, on a card that says "not connected" with nothing about which of six answers was wrong.
|
|
5671
|
+
*
|
|
5672
|
+
* `ok: false` is a REPORTED failure, not a transport error: the probe ran and the service said no. A card whose
|
|
5673
|
+
* settings cannot be checked from here at all answers `checked: false`, which is a different thing from a
|
|
5674
|
+
* failure and must never be drawn as one. */
|
|
5675
|
+
export const CapabilityProbeSchema = z.object({
|
|
5676
|
+
checked: z.boolean().describe("Whether this connection can be tested from here at all. False is not a failure: it is 'no test exists'."),
|
|
5677
|
+
ok: z.boolean().describe("Whether the service answered as itself."),
|
|
5678
|
+
message: z
|
|
5679
|
+
.string()
|
|
5680
|
+
.describe("What happened, in the words a person standing in front of the form needs: the service's own answer, or its refusal."),
|
|
5681
|
+
});
|
|
5682
|
+
export type CapabilityProbe = z.infer<typeof CapabilityProbeSchema>;
|
|
5683
|
+
|
|
5620
5684
|
// ---- hosts: the user's own connected computers (the `host` capability's live half) ----
|
|
5621
5685
|
// The manifest says which machines the user INTENDS to have connected; this says which are actually holding a
|
|
5622
5686
|
// socket right now. Nothing here is remembered across a daemon restart except the enrollment itself: a machine
|
|
@@ -7625,6 +7689,9 @@ export const PanelSummarySchema = z.object({
|
|
|
7625
7689
|
servers: z
|
|
7626
7690
|
.array(
|
|
7627
7691
|
z.object({
|
|
7692
|
+
// The port itself, not just the URL it appears in: forwarding one is how a repo answering on
|
|
7693
|
+
// several ports becomes previewable at all, and that call takes a number.
|
|
7694
|
+
port: z.number().describe("The port it is listening on, which is what forwarding it takes."),
|
|
7628
7695
|
url: z.string().describe("Where it answers, with the right scheme: a server on its own certificate is served over https."),
|
|
7629
7696
|
dir: z
|
|
7630
7697
|
.string()
|
|
@@ -7641,8 +7708,15 @@ export const PanelSummarySchema = z.object({
|
|
|
7641
7708
|
}),
|
|
7642
7709
|
)
|
|
7643
7710
|
.describe("Every server this repository is really serving, found by looking at what is listening. Empty when nothing answers."),
|
|
7644
|
-
|
|
7645
|
-
|
|
7711
|
+
/* https://preview-<repo>-<sandboxId>.<zone>, and ONLY where that address actually serves this repo: absent
|
|
7712
|
+
* on a sandbox with no zone or connect token (loopback/tests), and absent whenever the preview proxy has
|
|
7713
|
+
* nothing to route it to — nothing running, still starting, or (the ordinary monorepo) several dev servers
|
|
7714
|
+
* on ports of their own, none of which one hostname can stand for. Present ⇒ safe to open or frame, which
|
|
7715
|
+
* is what stops a surface from showing a 502 as if it were the app. */
|
|
7716
|
+
previewUrl: z
|
|
7717
|
+
.string()
|
|
7718
|
+
.optional()
|
|
7719
|
+
.describe("Where to open it from outside, present only while that address really serves it. Absent on a sandbox with no outside address."),
|
|
7646
7720
|
// The workspace role this repo dir occupies (the three fixed dirs); absent for extra clones.
|
|
7647
7721
|
role: z
|
|
7648
7722
|
.enum(["intent", "desired-state", "app"])
|
|
@@ -8323,28 +8397,28 @@ export const BrowserNameParamSchema = z.object({ name: z.string().describe("Whic
|
|
|
8323
8397
|
* (another agent, working, that you did not start):
|
|
8324
8398
|
* • `subagent`, the SDK's Agent/Task tool. The daemon learns of it from the SubagentStart/SubagentStop hooks
|
|
8325
8399
|
* and the task_* stream messages, joined on `toolUseId`.
|
|
8326
|
-
* • `
|
|
8327
|
-
*
|
|
8328
|
-
*
|
|
8329
|
-
*
|
|
8330
|
-
*
|
|
8331
|
-
* client
|
|
8332
|
-
*
|
|
8333
|
-
*
|
|
8334
|
-
* question they answer.
|
|
8335
|
-
*
|
|
8336
|
-
* WHAT A KIND CHANGES, and it is only ever the live view:
|
|
8337
|
-
* watching it means reading its transcript
|
|
8338
|
-
*
|
|
8339
|
-
export const SubagentKindSchema = z.enum(["subagent", "
|
|
8400
|
+
* • `spawned`, a full agent the turn started through the daemon's own spawn door (children/children.ts), on
|
|
8401
|
+
* ANY connected provider, Cursor's Composer, Codex, Gemini, another Claude. The daemon runs the child
|
|
8402
|
+
* itself, so its whole life is reported by direct calls rather than reconstructed from hooks or stdout.
|
|
8403
|
+
*
|
|
8404
|
+
* `id` IS THE SPAWNING TOOL CALL'S id for an SDK child (the one key its meta file, its task messages and the
|
|
8405
|
+
* client's `parentToolUseId` nesting all carry), and the child's own conversation id for a `spawned` one (the
|
|
8406
|
+
* spawn door returns it, so both sides hold it). A card links to its subagent with the id it has, and the
|
|
8407
|
+
* subagent points back at the card the same way. The ids the transcripts are actually READ with, the SDK's
|
|
8408
|
+
* agent id, stay daemon-side, because no surface asks a question they answer.
|
|
8409
|
+
*
|
|
8410
|
+
* WHAT A KIND CHANGES, and it is only ever the live view: an SDK subagent has no process of its own to look
|
|
8411
|
+
* at, so watching it means reading its transcript; a spawned child is a conversation of its own, so its live
|
|
8412
|
+
* view is that conversation's stream. */
|
|
8413
|
+
export const SubagentKindSchema = z.enum(["subagent", "spawned"]);
|
|
8340
8414
|
export type SubagentKind = z.infer<typeof SubagentKindSchema>;
|
|
8341
8415
|
|
|
8342
8416
|
// running/pending/blocked are live; the rest are terminal. Deliberately the SDK's own task vocabulary
|
|
8343
8417
|
// (SDKTaskUpdatedMessage.patch.status) rather than AgentStatus: this is not a fleet card's lifecycle (no
|
|
8344
8418
|
// draft/landed/conflict), and mapping the two would invent states neither side reports. `blocked` is the one
|
|
8345
|
-
// addition the SDK never says:
|
|
8346
|
-
//
|
|
8347
|
-
//
|
|
8419
|
+
// addition the SDK never says: a spawned child's own question/permission/plan card raises it
|
|
8420
|
+
// (children/children.ts), and it exists because "the child needs an answer" is the one live state a parent or
|
|
8421
|
+
// an operator acts on differently from "the child is working".
|
|
8348
8422
|
export const SubagentStatusSchema = z.enum(["pending", "running", "blocked", "completed", "failed", "killed", "paused"]);
|
|
8349
8423
|
export type SubagentStatus = z.infer<typeof SubagentStatusSchema>;
|
|
8350
8424
|
|
|
@@ -8352,20 +8426,23 @@ export const SubagentSessionSchema = z.object({
|
|
|
8352
8426
|
id: z
|
|
8353
8427
|
.string()
|
|
8354
8428
|
.describe(
|
|
8355
|
-
"The id of the tool call that started it
|
|
8429
|
+
"The id of the tool call that started it (an SDK child) or the child's own conversation id (a spawned one); either way both sides already hold it, so a card links to its helper with the id it has and the helper points back the same way.",
|
|
8356
8430
|
),
|
|
8357
8431
|
kind: SubagentKindSchema.describe(
|
|
8358
|
-
"What sort of helper: one the runtime spawned, or a
|
|
8432
|
+
"What sort of helper: one the runtime's own Task tool spawned in-process, or a full agent the daemon started for the turn. It changes only how you watch it.",
|
|
8359
8433
|
),
|
|
8360
8434
|
// The conversation whose turn spawned this, what the area groups its rows by, and the way back to the chat
|
|
8361
8435
|
// the card lives in.
|
|
8362
8436
|
conversationId: z.string().describe("The conversation whose turn started it, and the way back to the chat it belongs to."),
|
|
8363
|
-
// What it is and what it was asked to do: the subagent type (`Explore`, `general-purpose`) or
|
|
8364
|
-
//
|
|
8365
|
-
// `Explore · Locate claimIndexer definition`.
|
|
8437
|
+
// What it is and what it was asked to do: the subagent type (`Explore`, `general-purpose`) or a spawned
|
|
8438
|
+
// child's provider label, and the caller's one-line description. The area's row and the card's title read
|
|
8439
|
+
// as `Explore · Locate claimIndexer definition`.
|
|
8366
8440
|
agentType: z.string().optional().describe("What kind of helper it is."),
|
|
8367
8441
|
description: z.string().optional().describe("What it was asked to do, in one line."),
|
|
8368
8442
|
model: z.string().optional().describe("Which model it runs on."),
|
|
8443
|
+
// Which provider serves a `spawned` child (its AgentProvider id), so the row can wear the right logo. An
|
|
8444
|
+
// SDK subagent implies its own: it runs on its parent's provider.
|
|
8445
|
+
provider: z.string().optional().describe("Which provider serves it, for a helper spawned across providers."),
|
|
8369
8446
|
// How deep in the spawn tree (1 = spawned by the turn itself). From the SDK's meta.json; a subagent may
|
|
8370
8447
|
// itself delegate, and a flat list that cannot say so reads as though the turn started all of them.
|
|
8371
8448
|
spawnDepth: z
|
|
@@ -8403,14 +8480,6 @@ export const SubagentSessionSchema = z.object({
|
|
|
8403
8480
|
.optional()
|
|
8404
8481
|
.describe("Its report: what it concluded, without opening its record. The question a finished helper gets read for."),
|
|
8405
8482
|
error: z.string().optional().describe("Why it failed, when it did."),
|
|
8406
|
-
// A delegation's live view: the tmux session its command runs in. Absent for an SDK subagent, which has no
|
|
8407
|
-
// process of its own to attach to.
|
|
8408
|
-
terminal: z
|
|
8409
|
-
.string()
|
|
8410
|
-
.optional()
|
|
8411
|
-
.describe(
|
|
8412
|
-
"The terminal its command runs in, when there is one. Absent for a helper with no process of its own, which is watched by reading its record instead.",
|
|
8413
|
-
),
|
|
8414
8483
|
});
|
|
8415
8484
|
export type SubagentSession = z.infer<typeof SubagentSessionSchema>;
|
|
8416
8485
|
export const SubagentsListSchema = z.object({
|