@intentic/sandbox-contract 1.234.0 → 1.236.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/dist/chores/chores.d.ts.map +1 -1
- package/dist/chores/chores.js +2 -1
- package/dist/chores/chores.js.map +1 -1
- package/dist/chores/index.d.ts +1 -0
- package/dist/chores/index.d.ts.map +1 -1
- package/dist/chores/index.js +1 -0
- package/dist/chores/index.js.map +1 -1
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js +4 -3
- package/dist/chores/probes.js.map +1 -1
- package/dist/chores/workspace-scope.d.ts +4 -0
- package/dist/chores/workspace-scope.d.ts.map +1 -0
- package/dist/chores/workspace-scope.js +4 -0
- package/dist/chores/workspace-scope.js.map +1 -0
- package/dist/contracts/extensions.contract.d.ts +1 -2
- package/dist/contracts/extensions.contract.d.ts.map +1 -1
- package/dist/contracts/extensions.contract.js +5 -5
- package/dist/contracts/extensions.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +12 -0
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +10 -1
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/contracts/workspace.contract.d.ts +1 -0
- package/dist/contracts/workspace.contract.d.ts.map +1 -1
- package/dist/contracts/workspace.contract.js +2 -2
- package/dist/contracts/workspace.contract.js.map +1 -1
- package/dist/index.d.ts +14 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/ingress-contract.d.ts +23 -0
- package/dist/ingress-contract.d.ts.map +1 -0
- package/dist/ingress-contract.js +43 -0
- package/dist/ingress-contract.js.map +1 -0
- package/dist/ingress-protocol.d.ts +16 -0
- package/dist/ingress-protocol.d.ts.map +1 -0
- package/dist/ingress-protocol.js +263 -0
- package/dist/ingress-protocol.js.map +1 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +5 -5
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/computers.d.ts +41 -0
- package/dist/schemas/computers.d.ts.map +1 -1
- package/dist/schemas/computers.js +16 -0
- package/dist/schemas/computers.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +2 -3
- package/dist/schemas/extension-updates.d.ts.map +1 -1
- package/dist/schemas/extension-updates.js +4 -3
- package/dist/schemas/extension-updates.js.map +1 -1
- package/dist/schemas/workspace-tree.d.ts +1 -0
- package/dist/schemas/workspace-tree.d.ts.map +1 -1
- package/dist/schemas/workspace-tree.js +8 -1
- package/dist/schemas/workspace-tree.js.map +1 -1
- package/package.json +26 -4
- package/src/chores/chores.ts +2 -1
- package/src/chores/index.ts +1 -0
- package/src/chores/probes.test.ts +12 -0
- package/src/chores/probes.ts +5 -3
- package/src/chores/workspace-scope.ts +12 -0
- package/src/contracts/extensions.contract.ts +7 -6
- package/src/contracts/system.contract.ts +22 -1
- package/src/contracts/workspace.contract.ts +3 -2
- package/src/events.ts +2 -2
- package/src/ingress-contract.test.ts +65 -0
- package/src/ingress-contract.ts +155 -0
- package/src/ingress-protocol.test.ts +509 -0
- package/src/ingress-protocol.ts +574 -0
- package/src/schemas/agents.ts +20 -21
- package/src/schemas/computers.ts +60 -0
- package/src/schemas/extension-updates.ts +9 -7
- package/src/schemas/workspace-tree.ts +13 -4
- package/src/tunnel-ids.ts +4 -4
package/src/schemas/agents.ts
CHANGED
|
@@ -701,11 +701,11 @@ export const LandConflictPathSchema = z.object({
|
|
|
701
701
|
"Why it would not merge, and the three have nothing in common but the symptom. Your own uncommitted edits on that path, where yours is the copy at risk. The shared tree having moved under the conversation since it started, where nothing of yours is at risk. Or a file git cannot merge at all, where no automatic answer exists.",
|
|
702
702
|
),
|
|
703
703
|
});
|
|
704
|
-
/* land's
|
|
704
|
+
/* A composed land's refusal, grouped per repo. `paths` is the set that genuinely failed to apply. NOT the
|
|
705
705
|
* whole delta, which is what the first version reported whenever it could not pin the cause down, turning
|
|
706
|
-
* four real conflicts into a wall of fourteen. `clean` counts what
|
|
707
|
-
*
|
|
708
|
-
*
|
|
706
|
+
* four real conflicts into a wall of fourteen. `clean` counts what passed in that repo but stays on the branch
|
|
707
|
+
* with the rest of the composition. An empty `paths` with `clean: 0` is the repo-unavailable case: the main
|
|
708
|
+
* checkout is gone, and no path-level account exists. */
|
|
709
709
|
export const LandConflictSchema = z.object({
|
|
710
710
|
repo: z.string().describe("Which repository."),
|
|
711
711
|
paths: z
|
|
@@ -716,7 +716,7 @@ export const LandConflictSchema = z.object({
|
|
|
716
716
|
clean: z
|
|
717
717
|
.number()
|
|
718
718
|
.describe(
|
|
719
|
-
"How many files
|
|
719
|
+
"How many files in this repository passed but remain held with the refused composition. Zero alongside an empty list means the repository could not be reached at all.",
|
|
720
720
|
),
|
|
721
721
|
// The branch the user's checkout is on, the thing the agent has to rebase onto. Carried because only the
|
|
722
722
|
// daemon can see it: an isolated turn's worktree is mounted over the agent's whole view, so the resolution
|
|
@@ -730,13 +730,13 @@ export const LandConflictSchema = z.object({
|
|
|
730
730
|
),
|
|
731
731
|
});
|
|
732
732
|
export type LandConflict = z.infer<typeof LandConflictSchema>;
|
|
733
|
-
//
|
|
734
|
-
//
|
|
735
|
-
//
|
|
736
|
-
//
|
|
733
|
+
// Land's outcome for the whole frozen repo composition. The ordinary mode preflights every repo before it
|
|
734
|
+
// writes any main tree: landed means all of them applied, and a conflict means none did. Every worktree keeps
|
|
735
|
+
// its state, nothing is lost, and "Land now" stays available. `resolving` is populated only by a `merge` land:
|
|
736
|
+
// the paths written into the workspace carrying conflict markers, which the user finishes by hand.
|
|
737
737
|
export const LandResultSchema = z.object({
|
|
738
|
-
landed: z.boolean().describe("Whether
|
|
739
|
-
conflicts: z.array(LandConflictSchema).optional().describe("What stopped
|
|
738
|
+
landed: z.boolean().describe("Whether the entire composed change was applied."),
|
|
739
|
+
conflicts: z.array(LandConflictSchema).optional().describe("What stopped the whole composed change, grouped per repository."),
|
|
740
740
|
resolving: z
|
|
741
741
|
.array(
|
|
742
742
|
z.object({
|
|
@@ -745,7 +745,7 @@ export const LandResultSchema = z.object({
|
|
|
745
745
|
}),
|
|
746
746
|
)
|
|
747
747
|
.optional()
|
|
748
|
-
.describe("Files left half-merged
|
|
748
|
+
.describe("Files left half-merged when you asked to carry the whole composition with its conflicts marked for resolution."),
|
|
749
749
|
// A `measure` outcome with an outstanding delta: nothing was applied and nothing failed, the work is
|
|
750
750
|
// waiting on the branch for a deliberate Land. `landed: false` alone can't say that (it means refusal).
|
|
751
751
|
held: z
|
|
@@ -756,14 +756,13 @@ export const LandResultSchema = z.object({
|
|
|
756
756
|
),
|
|
757
757
|
});
|
|
758
758
|
export type LandResult = z.infer<typeof LandResultSchema>;
|
|
759
|
-
/*
|
|
760
|
-
* it applies, so a refusal leaves
|
|
761
|
-
* report offers, a three-way apply that
|
|
762
|
-
* resolve in place. It is opt-in because it
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
*
|
|
766
|
-
* on the branch for a deliberate Land. */
|
|
759
|
+
/* Land's input. `check` is the safe default: every repo is preflighted and the composition is applied only if
|
|
760
|
+
* ALL of it applies, so a refusal leaves every main tree byte-identical. `merge` is the escape hatch the
|
|
761
|
+
* conflict report offers, a three-way apply that carries the whole composition and leaves conflicted paths
|
|
762
|
+
* with markers to resolve in place. It is opt-in because it writes those markers. `measure` is the
|
|
763
|
+
* auto-land-off mode: everything a land does EXCEPT touching the main trees, the provenance commit onto
|
|
764
|
+
* agent/<id>, the cumulative diffstat, and the bookkeeping for work that reached main by another road, so a
|
|
765
|
+
* held agent's card stays current while its composed delta waits for a deliberate Land. */
|
|
767
766
|
export const LandModeSchema = z.enum(["check", "merge", "measure"]);
|
|
768
767
|
export type LandMode = z.infer<typeof LandModeSchema>;
|
|
769
768
|
/* WHICH RUNG OF AN AGENT'S HISTORY A READING, or a land. STARTS AT.
|
|
@@ -795,7 +794,7 @@ export type AgentSpan = z.infer<typeof AgentSpanSchema>;
|
|
|
795
794
|
export const AgentLandSchema = z.object({
|
|
796
795
|
id: z.string().min(1).describe("Which conversation's work to merge."),
|
|
797
796
|
mode: LandModeSchema.optional().describe(
|
|
798
|
-
"How to apply it. The default applies
|
|
797
|
+
"How to apply it. The default applies every repository or none, so a refusal leaves the workspace exactly as it was. The other carries the whole composition and leaves conflicted paths with markers to resolve by hand.",
|
|
799
798
|
),
|
|
800
799
|
span: AgentSpanSchema.optional().describe("How much of the work to take. Leave it out for everything not yet merged."),
|
|
801
800
|
force: z.boolean().optional().describe("Go ahead despite a check that would otherwise refuse."),
|
package/src/schemas/computers.ts
CHANGED
|
@@ -117,6 +117,53 @@ export const MachineFlowLineSchema = z.discriminatedUnion("kind", [
|
|
|
117
117
|
z.object({ kind: z.literal("error"), message: z.string() }),
|
|
118
118
|
]);
|
|
119
119
|
export type MachineFlowLine = z.infer<typeof MachineFlowLineSchema>;
|
|
120
|
+
/* RUNNING ONE OF THIS PRODUCT'S OWN CLIs ON A CONNECTED COMPUTER, FROM A BUTTON, with no agent in the loop.
|
|
121
|
+
*
|
|
122
|
+
* A machine that is connected as a computer can already be told things: the ops above drive its docker, and an
|
|
123
|
+
* agent with the `host` capability can run whatever it likes through `run_command`. What had no door was the
|
|
124
|
+
* ordinary case in between — the user wants the thing the CLI on their machine already does, and the sandbox is
|
|
125
|
+
* where they are looking. Their alternatives were to go and find a terminal, or to ask an agent to do it, which
|
|
126
|
+
* spends a turn and a model's judgement on a decision that has none in it.
|
|
127
|
+
*
|
|
128
|
+
* So: a CLOSED SET OF NAMES, and the argv is built on the daemon from the name alone (hosts/machine-commands.ts).
|
|
129
|
+
* The browser sends `mirror-off`, never a command line. That is the whole security property, and it is the
|
|
130
|
+
* reason this is an enum rather than a string: the same socket carries `run_command`, so a route that forwarded
|
|
131
|
+
* caller-supplied text would hand every browser session a shell on the user's laptop, which is a grant the
|
|
132
|
+
* capability card never made.
|
|
133
|
+
*
|
|
134
|
+
* The machine still enforces its own switches. "Run commands" being off comes back as its own refusal, in its
|
|
135
|
+
* own words, naming the control to flip — exactly as it does for the sandbox ops. */
|
|
136
|
+
export const MachineCommandSchema = z.enum(["mirror-off", "mirror-on"]);
|
|
137
|
+
export type MachineCommand = z.infer<typeof MachineCommandSchema>;
|
|
138
|
+
/* Which paired sandbox the command acts on: the machine's own id for it, as it appears in that machine's report,
|
|
139
|
+
* so nothing here has to re-derive the sanitizing the agent applied. Absent means every sandbox that machine
|
|
140
|
+
* pairs, which is what the CLI does when it is run bare.
|
|
141
|
+
*
|
|
142
|
+
* Pattern-bound because it becomes an argv token. It must start with an alphanumeric, not merely consist of id
|
|
143
|
+
* characters: a value like `--takeover` is made only of legal id characters and is a FLAG by the time the CLI on
|
|
144
|
+
* the machine parses it. Real ids are `sandbox-<hex>-<zone>`-shaped, so nothing legitimate leads with a dash. */
|
|
145
|
+
export const MachineSandboxIdSchema = z
|
|
146
|
+
.string()
|
|
147
|
+
.max(200)
|
|
148
|
+
.regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
|
|
149
|
+
export const MachineCommandInputSchema = z.object({
|
|
150
|
+
id: z.string().min(1),
|
|
151
|
+
command: MachineCommandSchema,
|
|
152
|
+
sandboxId: MachineSandboxIdSchema.optional(),
|
|
153
|
+
});
|
|
154
|
+
export type MachineCommandInput = z.infer<typeof MachineCommandInputSchema>;
|
|
155
|
+
/* What came back. `ok` is the command's own exit status, not this route's: a machine that refused the call, or a
|
|
156
|
+
* CLI that exited non-zero, is a real answer to show the person who clicked, not an exception to convert into
|
|
157
|
+
* one. Only an unreachable machine throws, because then there is nothing to report at all.
|
|
158
|
+
*
|
|
159
|
+
* `output` is what the command printed, kept because the CLI's own sentences ("Port mirroring OFF for: …") are
|
|
160
|
+
* better than anything this side would write over them. */
|
|
161
|
+
export const MachineCommandResultSchema = z.object({
|
|
162
|
+
ok: z.boolean(),
|
|
163
|
+
message: z.string(),
|
|
164
|
+
output: z.string().optional(),
|
|
165
|
+
});
|
|
166
|
+
export type MachineCommandResult = z.infer<typeof MachineCommandResultSchema>;
|
|
120
167
|
// One paired sandbox as the local agent holds it. `localDir` is the answer to the question the Desktop sync card
|
|
121
168
|
// has never been able to answer: which folder on that computer this sandbox's /work actually is.
|
|
122
169
|
export const MachinePairingSchema = z.object({
|
|
@@ -124,6 +171,19 @@ export const MachinePairingSchema = z.object({
|
|
|
124
171
|
mode: z.enum(["sync", "mirror"]),
|
|
125
172
|
// Set only for mode "sync", and only for the sandbox being reported to, see the redaction note above.
|
|
126
173
|
localDir: z.string().optional(),
|
|
174
|
+
/* Whether that computer is putting this sandbox's ports on its own localhost, which is a switch its owner
|
|
175
|
+
* holds and not a state this sandbox can read off anything else. An empty port list means two opposite
|
|
176
|
+
* things — nothing is listening in the sandbox, or the machine was told to keep them off — and only the
|
|
177
|
+
* second is worth a word on screen or a button to undo.
|
|
178
|
+
*
|
|
179
|
+
* The MACHINE owns the flag (the agent's `sync mirror off`), because the localhost being written to is
|
|
180
|
+
* there: a computer told to keep ports off must keep them off while this sandbox is asleep, unreachable, or
|
|
181
|
+
* arguing. A browser asks for it by running that same command over the machine's `host` capability, so the
|
|
182
|
+
* button and the CLI are one gesture rather than two mechanisms that can disagree.
|
|
183
|
+
*
|
|
184
|
+
* Optional because it is a fact only an agent new enough to have the switch reports; absent is read as "on",
|
|
185
|
+
* which is what mirroring has always been. */
|
|
186
|
+
mirroring: z.enum(["on", "off"]).optional(),
|
|
127
187
|
// Mutagen's own word for what the session is doing ("watching", "scanning", "transitioning", "halted-…").
|
|
128
188
|
// Carried verbatim rather than mapped to a traffic light: the halted states name their own cause, and a UI
|
|
129
189
|
// that reduces them to "problem" sends the user back to the terminal this report exists to replace.
|
|
@@ -299,13 +299,15 @@ export const WorkspaceExtensionCreatedSchema = z.object({
|
|
|
299
299
|
id: z.string().describe("The id it was given."),
|
|
300
300
|
dir: z.string().describe("Where its files are, so you can open them."),
|
|
301
301
|
});
|
|
302
|
-
/*
|
|
303
|
-
* report. Counts rather than events, and declared entries rather than concrete paths,
|
|
304
|
-
* ledger answers is "is this permission earned?": a finer record would be a log of
|
|
305
|
-
* indexed by extension, which is not a thing this product should be accumulating to
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
302
|
+
/* Batches of calls the host observed against extensions' declared routes: extension id → declared entry → how
|
|
303
|
+
* many since the last report. Counts rather than events, and declared entries rather than concrete paths,
|
|
304
|
+
* because the question the ledger answers is "is this permission earned?": a finer record would be a log of
|
|
305
|
+
* what the owner was doing, indexed by extension, which is not a thing this product should be accumulating to
|
|
306
|
+
* answer it. Every extension rides one request so the bookkeeping cannot become its own request burst. */
|
|
307
|
+
export const ExtensionUsageBatchSchema = z.object({
|
|
308
|
+
reports: z
|
|
309
|
+
.record(z.string(), z.record(z.string(), z.number().int().positive()))
|
|
310
|
+
.describe("Each extension that called something, and the counts against the declared powers it exercised."),
|
|
309
311
|
});
|
|
310
312
|
// One declared background process (contributes.processes), status/start/stop, addressed by the capability
|
|
311
313
|
// entry id + the manifest's process name. Undeclared names are NOT_FOUND, the manifest-honesty rule again.
|
|
@@ -101,16 +101,25 @@ export const WorkspaceTreeSchema = z.object({
|
|
|
101
101
|
hidden: z.number().describe("How many entries at the top level were cut for size. Zero means the listing is complete."),
|
|
102
102
|
});
|
|
103
103
|
export type WorkspaceTree = z.infer<typeof WorkspaceTreeSchema>;
|
|
104
|
-
// Lazy-load one directory's children, for a dir the tree walk listed but didn't descend into.
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// Lazy-load one directory's children, for a dir the tree walk listed but didn't descend into. The ordinary
|
|
105
|
+
// request is one level; a bounded `depth` lets a caller that genuinely needs a small subtree receive its
|
|
106
|
+
// descendants in the same flat `entries` list instead of issuing one request per directory.
|
|
107
107
|
export const WorkspaceChildrenQuerySchema = WorkspaceScopeSchema.extend({
|
|
108
108
|
path: z.string().min(1).describe("The folder to open, as a workspace path."),
|
|
109
|
+
depth: z.coerce
|
|
110
|
+
.number()
|
|
111
|
+
.int()
|
|
112
|
+
.min(1)
|
|
113
|
+
.max(5)
|
|
114
|
+
.optional()
|
|
115
|
+
.describe("How many levels to include. Omitted means direct children only; at most five levels can be read in one request."),
|
|
109
116
|
});
|
|
110
117
|
export const WorkspaceChildrenSchema = z.object({
|
|
111
118
|
entries: z
|
|
112
119
|
.array(WorkspaceTreeEntrySchema)
|
|
113
|
-
.describe(
|
|
120
|
+
.describe(
|
|
121
|
+
"What is inside it, as a flat list. With the default depth these are direct children; a deeper request also includes descendants, whose full paths say where they belong. Folders carry no nested contents of their own.",
|
|
122
|
+
),
|
|
114
123
|
hidden: z.number().describe("How many entries were cut for size. Zero means the listing is complete."),
|
|
115
124
|
});
|
|
116
125
|
export type WorkspaceChildren = z.infer<typeof WorkspaceChildrenSchema>;
|
package/src/tunnel-ids.ts
CHANGED
|
@@ -6,10 +6,10 @@ import { createHash } from "node:crypto";
|
|
|
6
6
|
export const sha256Hex = (value: string): string => createHash("sha256").update(value).digest("hex");
|
|
7
7
|
|
|
8
8
|
// The sandbox's stable 12-hex id, digested from the connect token. Used by:
|
|
9
|
-
// • the
|
|
10
|
-
// • the
|
|
11
|
-
// • the
|
|
12
|
-
// All
|
|
9
|
+
// • the hostname builders beside it (hostnames.ts) — every public name embeds this id
|
|
10
|
+
// • the platform's reachability grant mint and tunnelId lookup (reachability.ts, ingress-contract.ts)
|
|
11
|
+
// • the desktop agent's loopback-port derivation for the direct-dial shortcut (daemon-base.ts)
|
|
12
|
+
// All of them MUST agree on the digest, so it lives in the contract they share.
|
|
13
13
|
export const sandboxIdFromToken = (connectToken: string): string | undefined =>
|
|
14
14
|
connectToken === "" ? undefined : sha256Hex(connectToken).slice(0, 12);
|
|
15
15
|
|