@intentic/sandbox-contract 1.231.0 → 1.232.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 +6 -0
- package/dist/contracts/agent.contract.d.ts +11 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +10 -1
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/host.contract.d.ts +4 -0
- package/dist/contracts/host.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +106 -41
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +6 -1
- package/dist/contracts/runner.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +4 -0
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/definition.d.ts +961 -0
- package/dist/definition.d.ts.map +1 -0
- package/dist/definition.js +71 -0
- package/dist/definition.js.map +1 -0
- package/dist/events.d.ts +8 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +5 -0
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +132 -23
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +50 -14
- package/dist/schemas.js.map +1 -1
- package/dist/workspace-state.d.ts +9 -0
- package/dist/workspace-state.d.ts.map +1 -1
- package/dist/workspace-state.js +2 -0
- package/dist/workspace-state.js.map +1 -1
- package/package.json +6 -6
- package/src/contracts/agent.contract.ts +19 -0
- package/src/contracts/runner.contract.ts +17 -1
- package/src/definition.ts +171 -0
- package/src/events.ts +44 -0
- package/src/index.ts +1 -0
- package/src/schemas.ts +119 -28
- package/src/workspace-state.test.ts +4 -0
- package/src/workspace-state.ts +19 -0
package/src/schemas.ts
CHANGED
|
@@ -1937,6 +1937,23 @@ export const SteerSchema = z
|
|
|
1937
1937
|
// /agent fetch (which sends no cancel frame).
|
|
1938
1938
|
export const StopTurnSchema = z.object({ conversationId: z.string().min(1).describe("Which conversation's running turn to cancel.") });
|
|
1939
1939
|
|
|
1940
|
+
/* RUN THE HELD TURN AGAIN, and it carries a conversation id and NOTHING else, which is the entire point of it
|
|
1941
|
+
* existing as its own route rather than as a flag on a turn.
|
|
1942
|
+
*
|
|
1943
|
+
* A spent allowance leaves a turn stranded that the daemon still holds in full: the prompt, the attachments, the
|
|
1944
|
+
* model, the effort, the mode, the worktree, the session that holds whatever it managed to do. Every one of
|
|
1945
|
+
* those is on the turn the daemon already has, and a client that re-derived them from its own transcript would
|
|
1946
|
+
* be re-deriving them from the STRIPPED copy it renders (no preamble notes, no attachment note, no model), which
|
|
1947
|
+
* is how a re-send comes to run a different turn from the one it claims to repeat.
|
|
1948
|
+
*
|
|
1949
|
+
* So the caller says only WHICH conversation, and the daemon re-runs the turn it kept. What comes back is an
|
|
1950
|
+
* ordinary StartedTurn, and the caller then attaches to it exactly as it would to a turn somebody else started
|
|
1951
|
+
* (the resume note on the prompt is what tells an attaching window to reuse the bubble that is already there
|
|
1952
|
+
* instead of drawing the same message twice). */
|
|
1953
|
+
export const ResumeTurnSchema = z.object({
|
|
1954
|
+
conversationId: z.string().min(1).describe("Which conversation's held turn to run again."),
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1940
1957
|
// ---- claude rate-limit gate ----
|
|
1941
1958
|
// The GATE signal: whether the provider is letting turns through right now, and, when it is refusing, which
|
|
1942
1959
|
// window is binding and when it lifts. This is the SDK's rate_limit_event, mapped one-to-one, and it is only
|
|
@@ -7890,15 +7907,41 @@ export type MachineSandbox = z.infer<typeof MachineSandboxSchema>;
|
|
|
7890
7907
|
*
|
|
7891
7908
|
* The machine enforces which of them it will do: `sandboxes` covers everything but removal, which takes its own
|
|
7892
7909
|
* switch, and a refusal comes back as the machine's own sentence naming the control to flip. */
|
|
7893
|
-
|
|
7910
|
+
/* `runner-up` / `runner-remove` are the same door for a container that belongs to THIS SANDBOX rather than to
|
|
7911
|
+
* a person: a runner (runners/, docs/remote-runners-plan.md at the workspace root). They ride here because to
|
|
7912
|
+
* the machine they are the same act it already does, run and remove a sandbox container, and to the person
|
|
7913
|
+
* clicking they are the same row of buttons. Both take the `sandboxes` switch and neither takes the removal
|
|
7914
|
+
* one: a runner holds no workspace of its own, only a mirror of the parent's git, so removing it destroys
|
|
7915
|
+
* nothing the parent does not still have. */
|
|
7916
|
+
export const MachineSandboxOpSchema = z.enum([
|
|
7917
|
+
"start",
|
|
7918
|
+
"stop",
|
|
7919
|
+
"restart",
|
|
7920
|
+
"prepare",
|
|
7921
|
+
"update",
|
|
7922
|
+
"rebuild",
|
|
7923
|
+
"rollback",
|
|
7924
|
+
"remove",
|
|
7925
|
+
"logs",
|
|
7926
|
+
"runner-up",
|
|
7927
|
+
"runner-remove",
|
|
7928
|
+
]);
|
|
7894
7929
|
export type MachineSandboxOp = z.infer<typeof MachineSandboxOpSchema>;
|
|
7895
7930
|
|
|
7896
7931
|
export const MachineSandboxFlowSchema = z.object({
|
|
7897
7932
|
op: MachineSandboxOpSchema,
|
|
7933
|
+
// Which sandbox, or, for the two runner ops, which RUNNER: the name it is known by at both ends, the
|
|
7934
|
+
// parent's `/system/runners` list and the machine's `ic runner list`.
|
|
7898
7935
|
slug: z.string().min(1),
|
|
7899
7936
|
// The approved overlay's sha256, required by `rebuild` and meaningless to the rest. It is the trust anchor:
|
|
7900
7937
|
// only content that still hashes to what the owner reviewed is ever built.
|
|
7901
7938
|
hash: z.string().optional(),
|
|
7939
|
+
/* `runner-up` only, and both are filled in by the DAEMON, never by the caller: where the runner dials
|
|
7940
|
+
* (this sandbox's public URL) and the single-use pairing it redeems there. The browser asks for a runner
|
|
7941
|
+
* on a machine; it never holds the credential that makes one, which is what keeps a pairing out of every
|
|
7942
|
+
* surface between here and that machine. */
|
|
7943
|
+
parentUrl: z.string().optional(),
|
|
7944
|
+
pair: z.string().optional().meta({ secret: true }),
|
|
7902
7945
|
});
|
|
7903
7946
|
export type MachineSandboxFlow = z.infer<typeof MachineSandboxFlowSchema>;
|
|
7904
7947
|
|
|
@@ -8500,6 +8543,73 @@ export const SubagentIdParamSchema = z.object({ id: z.string() });
|
|
|
8500
8543
|
// proposal present with a hash different from custom's.
|
|
8501
8544
|
|
|
8502
8545
|
const environmentFileSchema = z.object({ content: z.string(), hash: z.string() });
|
|
8546
|
+
|
|
8547
|
+
/* ---- environment DRIFT: what the live container has that the image did not put there ----
|
|
8548
|
+
*
|
|
8549
|
+
* Anything installed outside /work dies with the container, and transcript mining showed the same tools being
|
|
8550
|
+
* reinstalled session after session (cargo-xwin six times, a Windows rustup target eight) before anyone thought
|
|
8551
|
+
* to bake them. Drift is the daemon OBSERVING that gap rather than trusting the model to report it: apt installs
|
|
8552
|
+
* read from dpkg's own log, everything else from system paths newer than the container itself. Two channels
|
|
8553
|
+
* because they are disjoint by construction — dpkg unpacks files with their archive mtimes, so an mtime sweep
|
|
8554
|
+
* cannot see apt, and nothing apt does lands under the swept prefixes' hand-installed corners. */
|
|
8555
|
+
export const EnvironmentDriftSchema = z.object({
|
|
8556
|
+
// When this container was created (PID 1's start). A snapshot whose bornAt is not the running container's
|
|
8557
|
+
// describes a container that no longer exists, and every reader must treat it as no drift at all.
|
|
8558
|
+
bornAt: z.number(),
|
|
8559
|
+
// When the probe ran.
|
|
8560
|
+
at: z.number(),
|
|
8561
|
+
// Debian packages installed since the container was born, from /var/log/dpkg.log.
|
|
8562
|
+
apt: z.array(z.string()),
|
|
8563
|
+
// System paths (outside /work) newer than the container, collapsed so a browser download is one entry.
|
|
8564
|
+
paths: z.array(z.string()),
|
|
8565
|
+
});
|
|
8566
|
+
export type EnvironmentDrift = z.infer<typeof EnvironmentDriftSchema>;
|
|
8567
|
+
|
|
8568
|
+
// How a runtime install was made, which decides whether the daemon can draft a Dockerfile step for it
|
|
8569
|
+
// mechanically (apt/cargo/npm/rustup-target) or only surface it for a person to route (pip belongs in a venv or
|
|
8570
|
+
// a Debian package, "other" is a curl|sh whose replay could embed anything).
|
|
8571
|
+
export const RuntimeInstallKindSchema = z.enum(["apt", "pip", "cargo", "npm", "rustup-target", "playwright", "gem", "pipx", "go", "other"]);
|
|
8572
|
+
export type RuntimeInstallKind = z.infer<typeof RuntimeInstallKindSchema>;
|
|
8573
|
+
|
|
8574
|
+
/* One tool's runtime-install history across sessions: the ledger entry behind the recurrence signal. Sessions
|
|
8575
|
+
* are the unit of recurrence — a session that retries an install five times needed it once — and the entry
|
|
8576
|
+
* survives container recreates (the file lives under /work), which is exactly what makes "installed again in a
|
|
8577
|
+
* fresh container" observable at all. */
|
|
8578
|
+
export const RuntimeInstallSchema = z.object({
|
|
8579
|
+
tool: z.string(),
|
|
8580
|
+
kind: RuntimeInstallKindSchema,
|
|
8581
|
+
// Distinct conversation ids that installed it, capped; length is the recurrence count that gates drafting.
|
|
8582
|
+
sessions: z.array(z.string()),
|
|
8583
|
+
// The most recent install commands, capped, secrets already masked to references by the harness.
|
|
8584
|
+
commands: z.array(z.string()),
|
|
8585
|
+
firstAt: z.number(),
|
|
8586
|
+
lastAt: z.number(),
|
|
8587
|
+
count: z.number(),
|
|
8588
|
+
// The owner rejected an auto-drafted step for this tool: never propose it again until this is cleared.
|
|
8589
|
+
declinedAt: z.number().optional(),
|
|
8590
|
+
});
|
|
8591
|
+
export type RuntimeInstall = z.infer<typeof RuntimeInstallSchema>;
|
|
8592
|
+
|
|
8593
|
+
export const RuntimeInstallsFileSchema = z.object({
|
|
8594
|
+
installs: z.array(RuntimeInstallSchema),
|
|
8595
|
+
// The last drift snapshot, persisted so a daemon restart does not blank the card until the next sweep.
|
|
8596
|
+
drift: EnvironmentDriftSchema.optional(),
|
|
8597
|
+
});
|
|
8598
|
+
export type RuntimeInstallsFile = z.infer<typeof RuntimeInstallsFileSchema>;
|
|
8599
|
+
|
|
8600
|
+
// A ledger entry as the Environment card shows it: recurrence joined with whether the install is present in the
|
|
8601
|
+
// LIVE container (drift-corroborated), already drafted for approval, or previously declined.
|
|
8602
|
+
export const EnvironmentRecurringSchema = z.object({
|
|
8603
|
+
tool: z.string(),
|
|
8604
|
+
kind: RuntimeInstallKindSchema,
|
|
8605
|
+
sessions: z.number(),
|
|
8606
|
+
lastAt: z.number(),
|
|
8607
|
+
live: z.boolean(),
|
|
8608
|
+
drafted: z.boolean().optional(),
|
|
8609
|
+
declined: z.boolean().optional(),
|
|
8610
|
+
});
|
|
8611
|
+
export type EnvironmentRecurring = z.infer<typeof EnvironmentRecurringSchema>;
|
|
8612
|
+
|
|
8503
8613
|
export const EnvironmentSchema = z.object({
|
|
8504
8614
|
proposal: environmentFileSchema.optional(),
|
|
8505
8615
|
// The owner-approved agent-written custom section (.intentic/config/environment.custom.Dockerfile).
|
|
@@ -8509,6 +8619,10 @@ export const EnvironmentSchema = z.object({
|
|
|
8509
8619
|
appliedHash: z.string().optional(),
|
|
8510
8620
|
// config.sandbox.name, the UI derives the rebuild one-liner's slug from it.
|
|
8511
8621
|
container: z.string().optional(),
|
|
8622
|
+
// What the live container has that the image did not put there; absent until the first sweep of this container.
|
|
8623
|
+
drift: EnvironmentDriftSchema.optional(),
|
|
8624
|
+
// Runtime installs worth the owner's attention: recurring across sessions, or present-and-doomed right now.
|
|
8625
|
+
recurring: z.array(EnvironmentRecurringSchema).optional(),
|
|
8512
8626
|
});
|
|
8513
8627
|
export type Environment = z.infer<typeof EnvironmentSchema>;
|
|
8514
8628
|
export const EnvironmentApproveSchema = z.object({ hash: z.string().min(1) });
|
|
@@ -8577,33 +8691,10 @@ export type EnvironmentContents = z.infer<typeof EnvironmentContentsSchema>;
|
|
|
8577
8691
|
* by entry in WORKSPACE_STATE_FILES / HISTORY_STATE_FILES. It cannot carry the other two, and the honest
|
|
8578
8692
|
* consequence is that an import ends in a REPORT rather than a claim of equivalence, the container has no
|
|
8579
8693
|
* docker socket, so only the host can rebuild the image the overlay describes.
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
// rather than trusting this, and uses it only to explain what is missing.
|
|
8585
|
-
export const BundleManifestSchema = z.object({
|
|
8586
|
-
// Bumped when the layout changes in a way an older daemon would misread. Refused rather than guessed at.
|
|
8587
|
-
version: z.literal(1),
|
|
8588
|
-
// Where it came from, for the report's first line. Never used to authorize anything.
|
|
8589
|
-
sandbox: z.object({ name: z.string() }).optional(),
|
|
8590
|
-
createdAt: z.number(),
|
|
8591
|
-
secrets: z.boolean(),
|
|
8592
|
-
/* The environment the target has to reproduce, carried as FACTS rather than as the composed file (which the
|
|
8593
|
-
* target recomposes against its OWN base image on first boot). `customDockerfile` is the owner-approved
|
|
8594
|
-
* source section; `capabilities` names what contributed the remaining fragments, so the report can list what
|
|
8595
|
-
* to re-add when the configs themselves did not travel. */
|
|
8596
|
-
environment: z.object({
|
|
8597
|
-
customDockerfile: z.string().optional(),
|
|
8598
|
-
baseImage: z.string().optional(),
|
|
8599
|
-
approvedHash: z.string().optional(),
|
|
8600
|
-
capabilities: z.array(z.object({ id: z.string(), kind: z.string() })),
|
|
8601
|
-
}),
|
|
8602
|
-
// Every path class the bundle deliberately left out, with the manifest's own note where it has one. This is
|
|
8603
|
-
// what turns "the export skipped things" from a silence into a list the owner can act on.
|
|
8604
|
-
excluded: z.array(z.object({ path: z.string(), portability: z.string(), note: z.string().optional() })),
|
|
8605
|
-
});
|
|
8606
|
-
export type BundleManifest = z.infer<typeof BundleManifestSchema>;
|
|
8694
|
+
*
|
|
8695
|
+
* The bundle's manifest (BundleManifestSchema) lives in definition.ts beside the sandbox DEFINITION it embeds:
|
|
8696
|
+
* a bundle is definition + state, and keeping the two schemas together is what keeps the two export doors from
|
|
8697
|
+
* drifting into different answers about what an environment is. */
|
|
8607
8698
|
|
|
8608
8699
|
// What a restore actually did. `needsAction` is the part that matters: the environment rebuild command, the
|
|
8609
8700
|
// credentials to re-enter, the logins to redo, each one a thing the target cannot do for itself.
|
|
@@ -343,6 +343,10 @@ describe(`VERSIONED_STATE_PATHS`, () => {
|
|
|
343
343
|
// The owner's per-extension update posture (notify / agent / auto): a standing decision about
|
|
344
344
|
// what may run unattended, which is exactly the kind of edit worth a line in `git log`.
|
|
345
345
|
`.intentic/config/extension-update-policy.json`,
|
|
346
|
+
// Which commands are heavy enough to take turns, and how many may run at once. Tracked because
|
|
347
|
+
// raising that limit is a decision about every session sharing the box, and `git log` is the only
|
|
348
|
+
// thing that answers "since when have we been allowing four of these at a time".
|
|
349
|
+
`.intentic/config/heavy-commands.json`,
|
|
346
350
|
`.intentic/config/loop-designs.json`,
|
|
347
351
|
`.intentic/config/personas.json`,
|
|
348
352
|
// A persona's own kit: the prompt it runs on and the skills only its turns reach. Tracked for the
|
package/src/workspace-state.ts
CHANGED
|
@@ -221,6 +221,18 @@ const STATE_FILES = [
|
|
|
221
221
|
},
|
|
222
222
|
|
|
223
223
|
{ path: ".intentic/config/settings.json", invalidates: ["settings", "manifests"], portability: "carry", versioned: true },
|
|
224
|
+
/* Which agent commands are heavy enough to take turns, and how many may run at once (the daemon reads it
|
|
225
|
+
* per Bash command: platform/heavy-commands.ts).
|
|
226
|
+
*
|
|
227
|
+
* `carry`, because the answer is a property of the WORKSPACE rather than of this machine: `pnpm test` fans
|
|
228
|
+
* out to the same 74 packages wherever the repo is cloned, so a fresh sandbox should arrive already knowing
|
|
229
|
+
* which commands to queue rather than rediscovering it by freezing once.
|
|
230
|
+
*
|
|
231
|
+
* `versioned` for the reason the config slice generally is (personas.json's entry argues it): the file
|
|
232
|
+
* changes at human speed, it holds no secret, and a change to it is exactly the kind a reviewer should see
|
|
233
|
+
* — raising the limit is a decision about everyone's sessions on that box, and `git log` is the only thing
|
|
234
|
+
* that answers "since when have we allowed four of these at once". */
|
|
235
|
+
{ path: ".intentic/config/heavy-commands.json", invalidates: ["settings"], portability: "carry", versioned: true },
|
|
224
236
|
// The rule table's last-fired stamps, beside the rules themselves. `derived` rather than `carry`: it is a
|
|
225
237
|
// record of what happened in THIS sandbox, and carrying it to a fresh one would date every rule to work
|
|
226
238
|
// that machine never did.
|
|
@@ -230,6 +242,13 @@ const STATE_FILES = [
|
|
|
230
242
|
portability: "derived",
|
|
231
243
|
note: "Stamps of when each rule last did something; the new sandbox starts its own record.",
|
|
232
244
|
},
|
|
245
|
+
/* The runtime-install ledger: which tools sessions installed into the container at runtime, how often, and
|
|
246
|
+
* the last drift snapshot (environment/runtime-installs.ts). `carry` where rule-firings chose `derived`,
|
|
247
|
+
* because the two record different subjects: a firing is about what THIS machine did, while the ledger is
|
|
248
|
+
* about what this WORKSPACE's tasks keep needing — a workspace moved to a fresh sandbox will hit the same
|
|
249
|
+
* missing tools, and arriving with the recurrence memory is the whole reason it is kept. The drift snapshot
|
|
250
|
+
* inside is machine-scoped, and self-expires on the move: its bornAt can never match the new container. */
|
|
251
|
+
{ path: ".intentic/records/runtime-installs.json", invalidates: ["environment"], portability: "carry" },
|
|
233
252
|
/* Written by the AGENT's file tools (the drafts skill), read by the owner's approval inbox, the one entry
|
|
234
253
|
* here whose whole point is that a change arrives from outside the browser that renders it. `authored`:
|
|
235
254
|
* a draft is text somebody wrote, and "find the reddit draft about X" is an ordinary search.
|