@intentic/sandbox-contract 1.330.0 → 1.332.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 +15 -0
- package/dist/chores/probes.d.ts.map +1 -1
- package/dist/chores/probes.js.map +1 -1
- package/dist/contracts/accounts.contract.d.ts +15 -0
- package/dist/contracts/accounts.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.d.ts +38 -0
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +14 -3
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +29 -0
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/automations.contract.d.ts +2 -0
- package/dist/contracts/automations.contract.d.ts.map +1 -1
- package/dist/contracts/device.contract.d.ts +7 -0
- package/dist/contracts/device.contract.d.ts.map +1 -1
- package/dist/contracts/git.contract.d.ts +2 -0
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/logs.contract.d.ts +10 -0
- package/dist/contracts/logs.contract.d.ts.map +1 -1
- package/dist/contracts/needs.contract.d.ts +7 -0
- package/dist/contracts/needs.contract.d.ts.map +1 -1
- package/dist/contracts/offload.contract.d.ts +70 -0
- package/dist/contracts/offload.contract.d.ts.map +1 -1
- package/dist/contracts/offload.contract.js +10 -1
- package/dist/contracts/offload.contract.js.map +1 -1
- package/dist/contracts/privacy.contract.d.ts +14 -0
- package/dist/contracts/privacy.contract.d.ts.map +1 -1
- package/dist/contracts/privacy.contract.js +11 -1
- package/dist/contracts/privacy.contract.js.map +1 -1
- package/dist/contracts/providers.contract.d.ts +3 -0
- package/dist/contracts/providers.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.d.ts +106 -2
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +1 -0
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +74 -94
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.js +19 -1
- package/dist/contracts/settings.contract.js.map +1 -1
- package/dist/contracts/system.contract.d.ts +20 -0
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/translator.contract.d.ts +8 -0
- package/dist/contracts/translator.contract.d.ts.map +1 -1
- package/dist/events/agent-events.d.ts +47 -0
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +6 -0
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/agent-words.d.ts.map +1 -1
- package/dist/events/agent-words.js +1 -1
- package/dist/events/agent-words.js.map +1 -1
- package/dist/events/resume.d.ts +3 -0
- package/dist/events/resume.d.ts.map +1 -1
- package/dist/events/resume.js +17 -2
- package/dist/events/resume.js.map +1 -1
- package/dist/events/sandbox-notice.d.ts +18 -0
- package/dist/events/sandbox-notice.d.ts.map +1 -1
- package/dist/events/sandbox-notice.js +27 -1
- package/dist/events/sandbox-notice.js.map +1 -1
- package/dist/events/system-events.d.ts +4 -0
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/transcript.d.ts +64 -0
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +17 -1
- package/dist/events/transcript.js.map +1 -1
- package/dist/index.d.ts +327 -124
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/models/agent-catalog.d.ts.map +1 -1
- package/dist/models/agent-catalog.js +8 -7
- package/dist/models/agent-catalog.js.map +1 -1
- package/dist/models/agent-runtimes.d.ts +3 -1
- package/dist/models/agent-runtimes.d.ts.map +1 -1
- package/dist/models/agent-runtimes.js +10 -0
- package/dist/models/agent-runtimes.js.map +1 -1
- package/dist/models/model-order.d.ts +1 -0
- package/dist/models/model-order.d.ts.map +1 -1
- package/dist/models/model-order.js +9 -0
- package/dist/models/model-order.js.map +1 -1
- package/dist/models/model-pins.d.ts.map +1 -1
- package/dist/models/model-pins.js.map +1 -1
- package/dist/models/model-roles.d.ts +32 -17
- package/dist/models/model-roles.d.ts.map +1 -1
- package/dist/models/model-roles.js +30 -26
- package/dist/models/model-roles.js.map +1 -1
- package/dist/models/model-setups.d.ts +46 -0
- package/dist/models/model-setups.d.ts.map +1 -0
- package/dist/models/model-setups.js +86 -0
- package/dist/models/model-setups.js.map +1 -0
- package/dist/models/plan-pools.d.ts +5 -1
- package/dist/models/plan-pools.d.ts.map +1 -1
- package/dist/models/plan-pools.js +41 -13
- package/dist/models/plan-pools.js.map +1 -1
- package/dist/models/provider-specs.d.ts +20 -0
- package/dist/models/provider-specs.d.ts.map +1 -1
- package/dist/models/provider-specs.js +13 -1
- package/dist/models/provider-specs.js.map +1 -1
- package/dist/policy/command-classes.d.ts.map +1 -1
- package/dist/policy/command-classes.js +26 -3
- package/dist/policy/command-classes.js.map +1 -1
- package/dist/policy/credential-material.d.ts +6 -0
- package/dist/policy/credential-material.d.ts.map +1 -1
- package/dist/policy/credential-material.js +37 -15
- package/dist/policy/credential-material.js.map +1 -1
- package/dist/policy/search-globs.d.ts +3 -0
- package/dist/policy/search-globs.d.ts.map +1 -1
- package/dist/policy/search-globs.js +118 -0
- package/dist/policy/search-globs.js.map +1 -1
- package/dist/policy/secret-hosts.d.ts +1 -0
- package/dist/policy/secret-hosts.d.ts.map +1 -1
- package/dist/policy/secret-hosts.js +1 -0
- package/dist/policy/secret-hosts.js.map +1 -1
- package/dist/policy/turned-away.d.ts.map +1 -1
- package/dist/policy/turned-away.js +1 -0
- package/dist/policy/turned-away.js.map +1 -1
- package/dist/protocol/doors/runner-protocol.d.ts +9 -0
- package/dist/protocol/doors/runner-protocol.d.ts.map +1 -1
- package/dist/protocol/doors/runner-protocol.js +5 -0
- package/dist/protocol/doors/runner-protocol.js.map +1 -1
- package/dist/protocol/pairing-redeem.d.ts +23 -0
- package/dist/protocol/pairing-redeem.d.ts.map +1 -0
- package/dist/protocol/pairing-redeem.js +44 -0
- package/dist/protocol/pairing-redeem.js.map +1 -0
- package/dist/protocol/peer-mcp-server.d.ts.map +1 -1
- package/dist/protocol/peer-mcp-server.js +1 -1
- package/dist/protocol/peer-mcp-server.js.map +1 -1
- package/dist/protocol/raw/raw-json-routes.d.ts +16 -0
- package/dist/protocol/raw/raw-json-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-json-routes.js +2 -0
- package/dist/protocol/raw/raw-json-routes.js.map +1 -1
- package/dist/protocol/raw/raw-routes.d.ts +8 -0
- package/dist/protocol/raw/raw-routes.d.ts.map +1 -1
- package/dist/protocol/raw/raw-routes.js +2 -0
- package/dist/protocol/raw/raw-routes.js.map +1 -1
- package/dist/protocol/sse.d.ts +20 -1
- package/dist/protocol/sse.d.ts.map +1 -1
- package/dist/protocol/sse.js +112 -17
- package/dist/protocol/sse.js.map +1 -1
- package/dist/schemas/agent.d.ts +35 -0
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +12 -1
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/automations.d.ts +6 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/automations.js +3 -1
- package/dist/schemas/automations.js.map +1 -1
- package/dist/schemas/devices.d.ts +17 -0
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +2 -1
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/engines.d.ts +8 -1
- package/dist/schemas/engines.d.ts.map +1 -1
- package/dist/schemas/engines.js +1 -1
- package/dist/schemas/engines.js.map +1 -1
- package/dist/schemas/environment.d.ts +30 -0
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js +13 -0
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/git/git-scope.d.ts +20 -0
- package/dist/schemas/git/git-scope.d.ts.map +1 -0
- package/dist/schemas/git/git-scope.js +37 -0
- package/dist/schemas/git/git-scope.js.map +1 -0
- package/dist/schemas/git/git.d.ts +1 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +9 -1
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +42 -0
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +3 -0
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/keep-warm.d.ts +1 -0
- package/dist/schemas/keep-warm.d.ts.map +1 -1
- package/dist/schemas/keep-warm.js +1 -1
- package/dist/schemas/keep-warm.js.map +1 -1
- package/dist/schemas/logs.d.ts +21 -0
- package/dist/schemas/logs.d.ts.map +1 -1
- package/dist/schemas/logs.js +19 -0
- package/dist/schemas/logs.js.map +1 -1
- package/dist/schemas/metrics.d.ts +12 -0
- package/dist/schemas/metrics.d.ts.map +1 -1
- package/dist/schemas/metrics.js +26 -8
- package/dist/schemas/metrics.js.map +1 -1
- package/dist/schemas/needs.d.ts +19 -0
- package/dist/schemas/needs.d.ts.map +1 -1
- package/dist/schemas/needs.js +17 -3
- package/dist/schemas/needs.js.map +1 -1
- package/dist/schemas/offload.d.ts +154 -0
- package/dist/schemas/offload.d.ts.map +1 -1
- package/dist/schemas/offload.js +34 -0
- package/dist/schemas/offload.js.map +1 -1
- package/dist/schemas/privacy.d.ts +19 -0
- package/dist/schemas/privacy.d.ts.map +1 -1
- package/dist/schemas/privacy.js +17 -2
- package/dist/schemas/privacy.js.map +1 -1
- package/dist/schemas/project-bring-back.d.ts +172 -0
- package/dist/schemas/project-bring-back.d.ts.map +1 -0
- package/dist/schemas/project-bring-back.js +39 -0
- package/dist/schemas/project-bring-back.js.map +1 -0
- package/dist/schemas/providers/handoff.d.ts +77 -0
- package/dist/schemas/providers/handoff.d.ts.map +1 -0
- package/dist/schemas/providers/handoff.js +85 -0
- package/dist/schemas/providers/handoff.js.map +1 -0
- package/dist/schemas/providers/plan-limits.d.ts +34 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +13 -2
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/providers/provider-oauth.d.ts +8 -0
- package/dist/schemas/providers/provider-oauth.d.ts.map +1 -1
- package/dist/schemas/providers/usage.d.ts +3 -1
- package/dist/schemas/providers/usage.d.ts.map +1 -1
- package/dist/schemas/providers/usage.js +7 -2
- package/dist/schemas/providers/usage.js.map +1 -1
- package/dist/schemas/run-targets.d.ts.map +1 -1
- package/dist/schemas/run-targets.js +5 -2
- package/dist/schemas/run-targets.js.map +1 -1
- package/dist/schemas/settings-history.d.ts +6 -1
- package/dist/schemas/settings-history.d.ts.map +1 -1
- package/dist/schemas/settings-history.js +25 -1
- package/dist/schemas/settings-history.js.map +1 -1
- package/dist/schemas/settings.d.ts +69 -100
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +41 -24
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/short-codes.d.ts +31 -0
- package/dist/schemas/short-codes.d.ts.map +1 -0
- package/dist/schemas/short-codes.js +28 -0
- package/dist/schemas/short-codes.js.map +1 -0
- package/dist/schemas/system.d.ts +130 -0
- package/dist/schemas/system.d.ts.map +1 -1
- package/dist/schemas/system.js +37 -1
- package/dist/schemas/system.js.map +1 -1
- package/dist/state/definition.d.ts +64 -40
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +1 -0
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +3 -0
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +1 -0
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/state/versions.d.ts +6 -0
- package/dist/state/versions.d.ts.map +1 -1
- package/dist/state/versions.js +47 -0
- package/dist/state/versions.js.map +1 -1
- package/dist/text/embed.d.ts +2 -1
- package/dist/text/embed.d.ts.map +1 -1
- package/dist/text/embed.js +10 -5
- package/dist/text/embed.js.map +1 -1
- package/dist/text/title.d.ts.map +1 -1
- package/dist/text/title.js +4 -7
- package/dist/text/title.js.map +1 -1
- package/dist/text/transcript-fold.d.ts +2 -1
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +21 -6
- package/dist/text/transcript-fold.js.map +1 -1
- package/package.json +28 -6
- package/src/chores/probes.ts +3 -1
- package/src/contracts/agent.contract.ts +24 -3
- package/src/contracts/offload.contract.ts +21 -1
- package/src/contracts/privacy.contract.ts +13 -0
- package/src/contracts/settings.contract.ts +23 -0
- package/src/events/agent-events.ts +13 -0
- package/src/events/agent-words.ts +2 -3
- package/src/events/resume.test.ts +14 -2
- package/src/events/resume.ts +32 -3
- package/src/events/sandbox-notice.ts +31 -1
- package/src/events/transcript.ts +27 -2
- package/src/index.ts +5 -0
- package/src/models/agent-catalog.test.ts +4 -3
- package/src/models/agent-catalog.ts +10 -7
- package/src/models/agent-runtimes.ts +30 -1
- package/src/models/capability-ledger.test.ts +4 -0
- package/src/models/model-order.ts +13 -0
- package/src/models/model-pins.ts +5 -4
- package/src/models/model-roles.test.ts +9 -31
- package/src/models/model-roles.ts +42 -49
- package/src/models/model-setups.test.ts +100 -0
- package/src/models/model-setups.ts +151 -0
- package/src/models/plan-pools.test.ts +85 -0
- package/src/models/plan-pools.ts +76 -17
- package/src/models/provider-specs.ts +20 -2
- package/src/policy/command-classes.ts +34 -3
- package/src/policy/credential-material.ts +58 -16
- package/src/policy/search-globs.ts +135 -0
- package/src/policy/secret-hosts.ts +7 -0
- package/src/policy/tests/command-classes.test.ts +35 -0
- package/src/policy/tests/credential-material.test.ts +20 -0
- package/src/policy/tests/search-globs.test.ts +46 -1
- package/src/policy/tests/secret-hosts.test.ts +17 -1
- package/src/policy/turned-away.ts +2 -0
- package/src/protocol/doors/runner-protocol.ts +14 -1
- package/src/protocol/pairing-redeem.test.ts +86 -0
- package/src/protocol/pairing-redeem.ts +79 -0
- package/src/protocol/peer-mcp-server.ts +1 -2
- package/src/protocol/raw/raw-json-routes.ts +3 -0
- package/src/protocol/raw/raw-routes.ts +5 -0
- package/src/protocol/sse.test.ts +50 -1
- package/src/protocol/sse.ts +172 -26
- package/src/schemas/agent.ts +23 -2
- package/src/schemas/automations.ts +6 -2
- package/src/schemas/daemon-health.test.ts +28 -0
- package/src/schemas/devices.ts +11 -2
- package/src/schemas/engines.ts +1 -1
- package/src/schemas/environment.ts +25 -0
- package/src/schemas/git/git-scope.test.ts +91 -0
- package/src/schemas/git/git-scope.ts +71 -0
- package/src/schemas/git/git.ts +15 -1
- package/src/schemas/hosts.ts +7 -0
- package/src/schemas/keep-warm.ts +2 -1
- package/src/schemas/logs.ts +25 -0
- package/src/schemas/metrics.test.ts +80 -0
- package/src/schemas/metrics.ts +31 -9
- package/src/schemas/needs.ts +29 -3
- package/src/schemas/offload.ts +76 -0
- package/src/schemas/privacy.ts +27 -3
- package/src/schemas/project-bring-back.test.ts +15 -0
- package/src/schemas/project-bring-back.ts +78 -0
- package/src/schemas/providers/handoff.test.ts +42 -0
- package/src/schemas/providers/handoff.ts +128 -0
- package/src/schemas/providers/plan-limits.ts +23 -2
- package/src/schemas/providers/usage.ts +13 -7
- package/src/schemas/run-targets.ts +5 -2
- package/src/schemas/settings-history.ts +43 -1
- package/src/schemas/settings.test.ts +84 -6
- package/src/schemas/settings.ts +74 -44
- package/src/schemas/short-codes.ts +48 -0
- package/src/schemas/system.ts +63 -1
- package/src/state/history-state.ts +4 -0
- package/src/state/runtime-state.test.ts +4 -0
- package/src/state/runtime-state.ts +4 -0
- package/src/state/versions.test.ts +30 -1
- package/src/state/versions.ts +68 -0
- package/src/text/embed.test.ts +14 -0
- package/src/text/embed.ts +19 -10
- package/src/text/title.ts +5 -9
- package/src/text/transcript-fold.test.ts +61 -0
- package/src/text/transcript-fold.ts +31 -6
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type GitChange, type GitDiffSide, type GitScope, UNATTRIBUTED_ORIGIN } from "./git.js";
|
|
2
|
+
|
|
3
|
+
// What a scope names, resolved against a repository's own status. One definition, read by the daemon when it acts on a
|
|
4
|
+
// scope and by the Changes panel when it asks the owner about one, so the question and the action cannot disagree.
|
|
5
|
+
// Sides are the verb's, not the scope's: staging reads the two not yet in the index; a scope narrows, never widens.
|
|
6
|
+
|
|
7
|
+
export interface ChangedSides {
|
|
8
|
+
readonly conflicted: readonly GitChange[];
|
|
9
|
+
readonly staged: readonly GitChange[];
|
|
10
|
+
readonly unstaged: readonly GitChange[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Who landed each path, newest first, as `RepoChanges.origins` ships it; a rename's old path is keyed too.
|
|
14
|
+
export type ChangeOrigins = Readonly<Record<string, readonly string[]>>;
|
|
15
|
+
|
|
16
|
+
// What each verb can move; `git add` on an unmerged path settles a merge, so conflicts are stageable too.
|
|
17
|
+
export const STAGEABLE_SIDES: readonly GitDiffSide[] = ["unstaged", "conflicted"];
|
|
18
|
+
export const UNSTAGEABLE_SIDES: readonly GitDiffSide[] = ["staged"];
|
|
19
|
+
// Discard rewrites the worktree, which every side has one of.
|
|
20
|
+
export const DISCARDABLE_SIDES: readonly GitDiffSide[] = ["conflicted", "staged", "unstaged"];
|
|
21
|
+
|
|
22
|
+
// The paths one row stands for on disk: a rename is two, and no verb acts on half a move.
|
|
23
|
+
export const legsOf = (change: Pick<GitChange, "path" | "from">): readonly string[] =>
|
|
24
|
+
change.from === undefined ? [change.path] : [change.path, change.from];
|
|
25
|
+
|
|
26
|
+
// Every conversation that landed either leg of a row, newest first per leg, each once.
|
|
27
|
+
export const originsOfChange = (change: Pick<GitChange, "path" | "from">, origins: ChangeOrigins): readonly string[] => [
|
|
28
|
+
...new Set(legsOf(change).flatMap((path) => origins[path] ?? [])),
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
// Whether a row is that origin's work: a conversation that landed either leg of it, or for the unattributed origin a row
|
|
32
|
+
// no conversation landed any leg of. A rename out of an agent's file is the agent's row, whoever renamed it, so the
|
|
33
|
+
// panel counts it, the scope moves it and the discard question names it under the same chip.
|
|
34
|
+
export const changeIsFrom = (change: Pick<GitChange, "path" | "from">, origin: string, origins: ChangeOrigins): boolean => {
|
|
35
|
+
const ids = originsOfChange(change, origins);
|
|
36
|
+
return origin === UNATTRIBUTED_ORIGIN ? ids.length === 0 : ids.includes(origin);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Paths a scope resolves to, for a verb that moves `sides`; both legs of a rename included.
|
|
40
|
+
// Deduplicated: a path staged and edited again is two rows over one file, and a rename's legs can be named twice.
|
|
41
|
+
export const scopedPaths = (changed: ChangedSides, sides: readonly GitDiffSide[], scope: GitScope, origins: ChangeOrigins = {}): readonly string[] => {
|
|
42
|
+
const origin = scope.origin;
|
|
43
|
+
const reading = scope.side === undefined ? sides : sides.filter((side) => side === scope.side);
|
|
44
|
+
const rows = reading.flatMap((side) => changed[side]);
|
|
45
|
+
const named = origin === undefined ? rows : rows.filter((change) => changeIsFrom(change, origin, origins));
|
|
46
|
+
return [...new Set(named.flatMap(legsOf))];
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// A scope that narrows nothing is the whole repository; two of the three verbs say so to git directly, unenumerated.
|
|
50
|
+
export const isWholeRepo = (scope: GitScope): boolean => scope.side === undefined && scope.origin === undefined;
|
|
51
|
+
|
|
52
|
+
// What discarding `paths` does to each, read off the same status: a path the last commit does not hold leaves the disk
|
|
53
|
+
// (an untracked file, a staged new file, a rename's new leg), every other returns to its committed state. Mirrors the
|
|
54
|
+
// daemon's discard, which unstages first and then cleans what reads untracked.
|
|
55
|
+
export const discardOutcome = (changed: ChangedSides, paths: readonly string[]): { readonly deletes: readonly string[]; readonly restores: readonly string[] } => {
|
|
56
|
+
const notInHead = new Set<string>();
|
|
57
|
+
for (const change of changed.staged) {
|
|
58
|
+
if (change.status === "added" || (change.status === "renamed" && change.from !== undefined)) {
|
|
59
|
+
notInHead.add(change.path);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Untracked, unless the index deleted it from the last commit: that path is in HEAD and comes back.
|
|
63
|
+
const indexDeleted = new Set(changed.staged.filter((change) => change.status === "deleted").map((change) => change.path));
|
|
64
|
+
for (const change of changed.unstaged) {
|
|
65
|
+
if (change.status === "added" && !indexDeleted.has(change.path)) {
|
|
66
|
+
notInHead.add(change.path);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const unique = [...new Set(paths)];
|
|
70
|
+
return { deletes: unique.filter((path) => notInHead.has(path)), restores: unique.filter((path) => !notInHead.has(path)) };
|
|
71
|
+
};
|
package/src/schemas/git/git.ts
CHANGED
|
@@ -78,9 +78,23 @@ export const DiscardSchema = RepoParamSchema.extend(GitTargetSchema.shape)
|
|
|
78
78
|
.refine(oneTarget, ONE_TARGET);
|
|
79
79
|
// Index-only moves; nothing on disk changes, so neither needs a checkpoint. An empty target means the whole repository
|
|
80
80
|
// (`git add -A`, or the entire index).
|
|
81
|
+
// `chip` is the reversible form of an origin's move (the Changes panel's origin chips): staging records what the index
|
|
82
|
+
// held at every path it moves, and unstaging the same chip writes exactly that back, so a partial `git add -p` or a
|
|
83
|
+
// merge's clean result survives one on/off. It names its own target, so it goes alone.
|
|
84
|
+
const oneIndexMove = (move: GitTarget & { readonly chip?: string | undefined }): boolean =>
|
|
85
|
+
oneTarget(move) && (move.chip === undefined || (move.paths === undefined && move.scope === undefined));
|
|
81
86
|
export const GitIndexMoveSchema = RepoParamSchema.extend(GitTargetSchema.shape)
|
|
87
|
+
.extend({
|
|
88
|
+
chip: z
|
|
89
|
+
.string()
|
|
90
|
+
.min(1)
|
|
91
|
+
.optional()
|
|
92
|
+
.describe(
|
|
93
|
+
`Stage one origin's unstaged files (a conversation id, or "${UNATTRIBUTED_ORIGIN}" for the files none landed) so the move can be taken back exactly: unstaging with the same value later restores what the index held at each of those paths before, partial staging included, and leaves alone any path staged differently since or a commit that already took them. Goes without paths or a scope.`,
|
|
94
|
+
),
|
|
95
|
+
})
|
|
82
96
|
.describe("What to move across the index. Nothing on disk changes either way.")
|
|
83
|
-
.refine(
|
|
97
|
+
.refine(oneIndexMove, { message: "name paths, a scope or a chip, not more than one" });
|
|
84
98
|
// No separate "set upstream" flag: the daemon runs `push -u` exactly when the branch has none yet, which is never
|
|
85
99
|
// destructive.
|
|
86
100
|
export const PushSchema = RepoParamSchema.extend({
|
package/src/schemas/hosts.ts
CHANGED
|
@@ -76,6 +76,13 @@ export const DeviceFactsSchema = z.object({
|
|
|
76
76
|
// The agent's last upkeep pass in this environment (DeviceUpkeepSchema): whether a release brought this machine to
|
|
77
77
|
// the current shape, read rather than assumed. Absent before the first pass, and from an agent older than the field.
|
|
78
78
|
upkeep: DeviceUpkeepSchema.optional(),
|
|
79
|
+
// How the agent before this one ended, as one sentence (watchdog kill, crash, update, reboot), so a device that
|
|
80
|
+
// dropped off says why once it is back. Absent on a first start, and from an agent older than the field.
|
|
81
|
+
previousExit: z.string().optional(),
|
|
82
|
+
// Which ic answers on this machine and what steers it: an old ic.exe installed from a leftover IC_URL looked like
|
|
83
|
+
// a daemon bug for an afternoon. Override values are the agent's own redaction (secret-looking ones by length).
|
|
84
|
+
ic: z.object({ path: z.string(), version: z.string(), modified: z.string().optional() }).optional(),
|
|
85
|
+
icOverrides: z.array(z.string()).optional(),
|
|
79
86
|
});
|
|
80
87
|
export type DeviceFacts = z.infer<typeof DeviceFactsSchema>;
|
|
81
88
|
|
package/src/schemas/keep-warm.ts
CHANGED
|
@@ -88,7 +88,8 @@ export const PromptCacheOpeningSchema = z.object({
|
|
|
88
88
|
});
|
|
89
89
|
export type PromptCacheOpening = z.infer<typeof PromptCacheOpeningSchema>;
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
/** A token count as the transcript words it: "305k", or the bare count under a thousand. */
|
|
92
|
+
export const tokensLabel = (tokens: number): string => (tokens >= 1_000 ? `${Math.round(tokens / 1_000)}k` : String(tokens));
|
|
92
93
|
|
|
93
94
|
const spanLabel = (ms: number): string => {
|
|
94
95
|
const minutes = Math.max(1, Math.round(ms / 60_000));
|
package/src/schemas/logs.ts
CHANGED
|
@@ -52,10 +52,35 @@ export const ClientDiagnosticSchema = z.object({
|
|
|
52
52
|
.describe("Whatever else was worth keeping."),
|
|
53
53
|
});
|
|
54
54
|
export type ClientDiagnostic = z.infer<typeof ClientDiagnosticSchema>;
|
|
55
|
+
// Which page a batch came from, said once per batch rather than on every event. Without it two tabs' reports read as
|
|
56
|
+
// one stream, and an agent spent dozens of calls learning a page was the owner's own dev server rather than the build
|
|
57
|
+
// it had just changed.
|
|
58
|
+
export const ClientPageSchema = z.object({
|
|
59
|
+
pageId: z.string().min(1).max(64).describe("This page load's own id: every report from one load shares it, and a reload gets a new one."),
|
|
60
|
+
tabId: z.string().max(64).optional().describe("The browser tab's id, which a reload of the same tab keeps."),
|
|
61
|
+
userAgent: z.string().max(300).optional().describe("The browser's own description of itself."),
|
|
62
|
+
viewport: z.string().max(40).optional().describe("The window's size in CSS pixels and its pixel ratio, e.g. 390x844@3."),
|
|
63
|
+
// The join key into PostHog's session replay: absent where analytics is off (dev, self-hosted) or not loaded yet.
|
|
64
|
+
replay: z.string().max(100).optional().describe("The PostHog session id, whose replay shows what the person did."),
|
|
65
|
+
origin: z.string().max(200).optional().describe("The address the page was loaded from."),
|
|
66
|
+
served: z
|
|
67
|
+
.string()
|
|
68
|
+
.max(300)
|
|
69
|
+
.optional()
|
|
70
|
+
.describe("How the page was served: `dev` (a Vite dev server, with the checkout, branch and who started it), `desktop` (the installed app, with its version), `local` (a window on a folder) or `hosted`."),
|
|
71
|
+
commit: z
|
|
72
|
+
.string()
|
|
73
|
+
.max(40)
|
|
74
|
+
.optional()
|
|
75
|
+
.describe("The short commit the page's build was made from (a dev server's: the one it started on). Absent where the build could not read it."),
|
|
76
|
+
});
|
|
77
|
+
export type ClientPage = z.infer<typeof ClientPageSchema>;
|
|
55
78
|
// Capped batch: the client already coalesces and drops its own side, and an unbounded array here is a way to fill a
|
|
56
79
|
// disk with one request.
|
|
57
80
|
export const ClientDiagnosticsReportSchema = z.object({
|
|
58
81
|
events: z.array(ClientDiagnosticSchema).min(1).max(50).describe("What the browser has to report, oldest first."),
|
|
82
|
+
// Optional: a page from before it sends none.
|
|
83
|
+
page: ClientPageSchema.optional().describe("Which page load sent the batch."),
|
|
59
84
|
});
|
|
60
85
|
export type ClientDiagnosticsReport = z.infer<typeof ClientDiagnosticsReportSchema>;
|
|
61
86
|
export const ClientDiagnosticsAcceptedSchema = z.object({
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type SandboxMetrics, SandboxMetricsSchema } from "./metrics.js";
|
|
2
|
+
|
|
3
|
+
// A daemon payload from before memoryBreakdown and the other optional accounting fields existed.
|
|
4
|
+
const legacyMetrics = {
|
|
5
|
+
at: 1_000_000,
|
|
6
|
+
sandbox: {
|
|
7
|
+
cores: 2,
|
|
8
|
+
memoryBytes: 7 * 2 ** 30,
|
|
9
|
+
memoryLimitBytes: 16 * 2 ** 30,
|
|
10
|
+
loadAverage: [1.5, 1.25, 1],
|
|
11
|
+
processes: 3,
|
|
12
|
+
},
|
|
13
|
+
daemon: { rssBytes: 300 * 2 ** 20, heapUsedBytes: 120 * 2 ** 20 },
|
|
14
|
+
sessions: { "conv-a": { processes: 2, rssBytes: 400 * 2 ** 20 } },
|
|
15
|
+
roles: {
|
|
16
|
+
agentRuntime: { processes: 1, rssBytes: 300 * 2 ** 20 },
|
|
17
|
+
terminal: { processes: 1, rssBytes: 100 * 2 ** 20 },
|
|
18
|
+
},
|
|
19
|
+
} satisfies SandboxMetrics;
|
|
20
|
+
|
|
21
|
+
describe("the metrics memory accounting contract", () => {
|
|
22
|
+
test("an older payload parses unchanged without the optional breakdown or its categories", () => {
|
|
23
|
+
const parsed = SandboxMetricsSchema.parse(legacyMetrics);
|
|
24
|
+
expect(parsed).toEqual(legacyMetrics);
|
|
25
|
+
expect(Object.hasOwn(parsed.sandbox, "memoryBreakdown")).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("a partial new breakdown preserves zero and leaves unmeasured categories absent", () => {
|
|
29
|
+
const payload = {
|
|
30
|
+
...legacyMetrics,
|
|
31
|
+
sandbox: {
|
|
32
|
+
...legacyMetrics.sandbox,
|
|
33
|
+
memoryBreakdown: { anonymousBytes: 0, inactiveFileCacheBytes: 3 * 2 ** 30 },
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
expect(SandboxMetricsSchema.parse(payload)).toEqual(payload);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("each category is independently optional, with no zero defaults for the others", () => {
|
|
40
|
+
for (const memoryBreakdown of [
|
|
41
|
+
{},
|
|
42
|
+
{ anonymousBytes: 0 },
|
|
43
|
+
{ countedFileCacheBytes: 0 },
|
|
44
|
+
{ kernelBytes: 0 },
|
|
45
|
+
{ inactiveFileCacheBytes: 0 },
|
|
46
|
+
]) {
|
|
47
|
+
const payload = { ...legacyMetrics, sandbox: { ...legacyMetrics.sandbox, memoryBreakdown } };
|
|
48
|
+
expect(SandboxMetricsSchema.parse(payload)).toEqual(payload);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("measured resident categories remain separate from a headline that also counts full swap", () => {
|
|
53
|
+
const payload = {
|
|
54
|
+
...legacyMetrics,
|
|
55
|
+
sandbox: {
|
|
56
|
+
...legacyMetrics.sandbox,
|
|
57
|
+
memoryBytes: 16.5 * 2 ** 30,
|
|
58
|
+
swapBytes: 9.5 * 2 ** 30,
|
|
59
|
+
swapLimitBytes: 10 * 2 ** 30,
|
|
60
|
+
swapFull: true,
|
|
61
|
+
memoryBreakdown: {
|
|
62
|
+
anonymousBytes: 5 * 2 ** 30,
|
|
63
|
+
countedFileCacheBytes: 2 ** 30,
|
|
64
|
+
kernelBytes: 2 ** 30,
|
|
65
|
+
inactiveFileCacheBytes: 3 * 2 ** 30,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
expect(SandboxMetricsSchema.parse(payload)).toEqual(payload);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("present categories must be byte numbers, not numeric strings or non-finite values", () => {
|
|
73
|
+
for (const field of ["anonymousBytes", "countedFileCacheBytes", "kernelBytes", "inactiveFileCacheBytes"]) {
|
|
74
|
+
for (const invalid of ["0", null, Number.NaN, Number.POSITIVE_INFINITY]) {
|
|
75
|
+
const payload = { ...legacyMetrics, sandbox: { ...legacyMetrics.sandbox, memoryBreakdown: { [field]: invalid } } };
|
|
76
|
+
expect(SandboxMetricsSchema.safeParse(payload).success).toBe(false);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
package/src/schemas/metrics.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
// What the sandbox is using right now, measured only when asked (GET /system/metrics). CPU percentages are of ONE core
|
|
4
|
-
// unless the field says otherwise; memory is
|
|
4
|
+
// unless the field says otherwise; memory is in bytes, with admission accounting and process RSS distinguished below.
|
|
5
5
|
|
|
6
6
|
// The kinds of process a sandbox runs, as the daemon classifies them by command line.
|
|
7
7
|
export const PROCESS_ROLES = [
|
|
@@ -30,7 +30,7 @@ export const ProcessGroupMetricsSchema = z.object({
|
|
|
30
30
|
rssBytes: z
|
|
31
31
|
.number()
|
|
32
32
|
.describe(
|
|
33
|
-
"Their resident memory added up, in bytes
|
|
33
|
+
"Their process resident memory (RSS) added up, in bytes, including file mappings and shared pages counted once per process. This can exceed what they cost together. It excludes unmapped file cache, kernel memory and swapped-out pages.",
|
|
34
34
|
),
|
|
35
35
|
});
|
|
36
36
|
export type ProcessGroupMetrics = z.infer<typeof ProcessGroupMetricsSchema>;
|
|
@@ -84,24 +84,46 @@ export const SandboxUsageSchema = z.object({
|
|
|
84
84
|
memoryBytes: z
|
|
85
85
|
.number()
|
|
86
86
|
.describe(
|
|
87
|
-
"Memory counted against the limit, as the daemon admits work by it: resident
|
|
87
|
+
"Memory counted against the limit, as the daemon admits work by it: the cgroup's resident charge, including the daemon, kernel memory and file cache except inactive_file, plus swapped bytes only when swap is nearly full (`swapFull`). Falls back to the machine's used memory when the budget cannot measure it.",
|
|
88
88
|
),
|
|
89
89
|
memoryLimitBytes: z
|
|
90
90
|
.number()
|
|
91
91
|
.describe(
|
|
92
92
|
"The memory limit work is admitted against: where the kernel starts throttling the container (memory.high), else its hard limit, else the machine's memory.",
|
|
93
93
|
),
|
|
94
|
+
memoryBreakdown: z
|
|
95
|
+
.object({
|
|
96
|
+
anonymousBytes: z.number().optional().describe("Anonymous resident memory charged to the cgroup (memory.stat anon), in bytes."),
|
|
97
|
+
countedFileCacheBytes: z
|
|
98
|
+
.number()
|
|
99
|
+
.optional()
|
|
100
|
+
.describe("File cache counted in resident usage (memory.stat file less inactive_file), in bytes. Absent unless both counters are valid."),
|
|
101
|
+
kernelBytes: z
|
|
102
|
+
.number()
|
|
103
|
+
.optional()
|
|
104
|
+
.describe("Kernel memory charged to the cgroup (memory.stat kernel), in bytes. Absent on kernels without that aggregate."),
|
|
105
|
+
inactiveFileCacheBytes: z
|
|
106
|
+
.number()
|
|
107
|
+
.optional()
|
|
108
|
+
.describe("Inactive file cache (memory.stat inactive_file), in bytes, excluded from memoryBytes."),
|
|
109
|
+
})
|
|
110
|
+
.optional()
|
|
111
|
+
.describe(
|
|
112
|
+
"Approximate measured memory.stat categories, not an RSS subtraction or an exact sum of memoryBytes: admission is sampled separately and may also count swap. Missing or invalid categories are omitted, not zero. Absent without cgroup memory, on machine-memory fallback, when no category is valid, and from older daemons.",
|
|
113
|
+
),
|
|
94
114
|
swapBytes: z
|
|
95
115
|
.number()
|
|
96
116
|
.optional()
|
|
97
117
|
.describe(
|
|
98
|
-
"What was pushed out to swap, counted in `memoryBytes` only
|
|
118
|
+
"What was pushed out to swap, counted in `memoryBytes` only when swap is nearly full under admission policy. Absent where the sandbox cannot see its own memory.",
|
|
99
119
|
),
|
|
100
120
|
swapLimitBytes: z.number().optional().describe("What swap can hold. Absent where nothing bounds it, and from a daemon that predates it."),
|
|
101
121
|
swapFull: z
|
|
102
122
|
.boolean()
|
|
103
123
|
.optional()
|
|
104
|
-
.describe(
|
|
124
|
+
.describe(
|
|
125
|
+
"Swap has reached the admission policy's nearly-full threshold against `swapLimitBytes`, so swapped bytes count against the memory limit too, even if swap space remains.",
|
|
126
|
+
),
|
|
105
127
|
memoryRoom: MemoryRoomSchema.optional().describe("What admission reads off the same reading. Absent from a daemon that predates it."),
|
|
106
128
|
diskBytes: z.number().optional().describe("Space used on the volume the workspace lives on. Absent when the volume would not say."),
|
|
107
129
|
diskTotalBytes: z.number().optional().describe("That volume's size. Absent when the volume would not say."),
|
|
@@ -114,7 +136,7 @@ export const SandboxUsageSchema = z.object({
|
|
|
114
136
|
.describe(
|
|
115
137
|
"Cores the machine's load average reads against: every core the sandbox may be scheduled on, before any quota. Absent from a daemon that predates it.",
|
|
116
138
|
),
|
|
117
|
-
processes: z.number().describe("How many processes are running in the sandbox."),
|
|
139
|
+
processes: z.number().describe("How many processes are running in the sandbox, including the daemon."),
|
|
118
140
|
pressure: PressureMetricsSchema.optional().describe(
|
|
119
141
|
"How much work waited on CPU, memory or disk lately. Absent where the kernel does not report it.",
|
|
120
142
|
),
|
|
@@ -140,12 +162,12 @@ export const SandboxMetricsSchema = z.object({
|
|
|
140
162
|
.number()
|
|
141
163
|
.optional()
|
|
142
164
|
.describe("How long the CPU figures were measured over, in milliseconds. Absent on a first reading, and then so is every CPU figure."),
|
|
143
|
-
sandbox: SandboxUsageSchema.describe("The sandbox as a whole."),
|
|
144
|
-
daemon: DaemonUsageSchema.describe("The daemon
|
|
165
|
+
sandbox: SandboxUsageSchema.describe("The sandbox as a whole, including the daemon."),
|
|
166
|
+
daemon: DaemonUsageSchema.describe("The daemon's own usage, included in the sandbox total and process count but excluded from roles and session sums."),
|
|
145
167
|
sessions: z
|
|
146
168
|
.record(z.string(), SessionMetricsSchema)
|
|
147
169
|
.describe(
|
|
148
|
-
"What each conversation's processes use, by conversation id: the agent's own process and everything it started. Only conversations with processes running, and only those the caller may see.",
|
|
170
|
+
"What each conversation's processes use, by conversation id: the agent's own process and everything it started, excluding the daemon. These group many of the same process readings as roles, not additional memory. Only conversations with processes running, and only those the caller may see.",
|
|
149
171
|
),
|
|
150
172
|
roles: z
|
|
151
173
|
.partialRecord(ProcessRoleSchema, ProcessGroupMetricsSchema)
|
package/src/schemas/needs.ts
CHANGED
|
@@ -57,6 +57,13 @@ export type CapabilityNeed = z.infer<typeof CapabilityNeedSchema>;
|
|
|
57
57
|
// The alphabet a `{{secret:NAME}}` reference resolves; a pasted value is stored under exactly this name.
|
|
58
58
|
export const SECRET_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
|
|
59
59
|
|
|
60
|
+
// A `{{secret:name}}` reference wherever it stands in text, capturing the name. Wider than SECRET_NAME, since a
|
|
61
|
+
// reference also names the other stores' entries (`<capability>/<field>`). Not global, so a shared instance carries no
|
|
62
|
+
// `lastIndex` between callers: a site that replaces or spans every one derives its own /g copy from it. The terminal
|
|
63
|
+
// filter (@intentic/output-cleaners, builtins only) keeps a copy, pinned to this one by the daemon's
|
|
64
|
+
// src/secrets/terminal-masking-parity.test.ts.
|
|
65
|
+
export const SECRET_REFERENCE = /\{\{secret:([A-Za-z0-9_./-]+)\}\}/;
|
|
66
|
+
|
|
60
67
|
export const SecretNeedSchema = z.object({
|
|
61
68
|
kind: z.literal("secret"),
|
|
62
69
|
name: z.string().regex(SECRET_NAME).describe("The name it is stored under, and what `{{secret:NAME}}` will resolve."),
|
|
@@ -283,6 +290,16 @@ export const AgentNeedSchema = z.object({
|
|
|
283
290
|
});
|
|
284
291
|
export type AgentNeed = z.infer<typeof AgentNeedSchema>;
|
|
285
292
|
|
|
293
|
+
// A credential gateway card's "Allow requests like this with <name> in this conversation": the `ask` rule it answered
|
|
294
|
+
// stops asking that conversation, until somebody takes it back. Kept by the rule's own content, so editing the rule
|
|
295
|
+
// asks again.
|
|
296
|
+
export const BrokerRulePassSchema = z.object({
|
|
297
|
+
capability: z.string().describe("The capability whose rule asked, by id."),
|
|
298
|
+
rule: z.string().describe("The rule's own identity (its content), so an edited rule is a different one and asks again."),
|
|
299
|
+
said: z.string().describe('The request in a person\'s words, as the card put it: "GET api.github.com/repos/x with GitHub".'),
|
|
300
|
+
});
|
|
301
|
+
export type BrokerRulePass = z.infer<typeof BrokerRulePassSchema>;
|
|
302
|
+
|
|
286
303
|
// What a person allowed one conversation beyond its persona and area, from a grant need's card
|
|
287
304
|
// (personas/conversation-grants.ts). Honoured from that conversation's next turn on, and revocable on the Grants page.
|
|
288
305
|
export const ConversationGrantSchema = z.object({
|
|
@@ -306,6 +323,11 @@ export const ConversationGrantSchema = z.object({
|
|
|
306
323
|
.boolean()
|
|
307
324
|
.default(false)
|
|
308
325
|
.describe("Whether every request an allow-once could settle is allowed without asking, until somebody takes it back."),
|
|
326
|
+
// Answered on a credential gateway card, read live by the gateway as each request its rules would ask about arrives.
|
|
327
|
+
rules: z
|
|
328
|
+
.array(BrokerRulePassSchema)
|
|
329
|
+
.default([])
|
|
330
|
+
.describe("Credential gateway rules that no longer ask before its requests, each with the words a person allowed it in."),
|
|
309
331
|
updatedAt: z.number().describe("When it last changed, in milliseconds."),
|
|
310
332
|
by: z.string().optional().describe("Who last allowed something here."),
|
|
311
333
|
});
|
|
@@ -323,6 +345,7 @@ export const StandingGrantsSchema = z.object({
|
|
|
323
345
|
installs: z.boolean().default(false).describe("Whether its own dependency installs run without asking."),
|
|
324
346
|
secrets: z.array(z.string()).default([]).describe("Secrets it may send past their host guard without asking."),
|
|
325
347
|
everything: z.boolean().default(false).describe("Whether every request an allow-once could settle is allowed without asking."),
|
|
348
|
+
rules: z.array(BrokerRulePassSchema).default([]).describe("Credential gateway rules that no longer ask before its requests."),
|
|
326
349
|
by: z.string().optional().describe("Who last allowed one of those."),
|
|
327
350
|
updatedAt: z.number().optional().describe("When one of those last changed, in milliseconds."),
|
|
328
351
|
releases: z
|
|
@@ -336,11 +359,14 @@ export type StandingGrants = z.infer<typeof StandingGrantsSchema>;
|
|
|
336
359
|
export const GrantRevokeSchema = z.object({
|
|
337
360
|
conversationId: z.string(),
|
|
338
361
|
kind: z
|
|
339
|
-
.enum(["capability", "folder", "shelf", "release", "install", "secret", "everything"])
|
|
362
|
+
.enum(["capability", "folder", "shelf", "release", "install", "secret", "everything", "rule"])
|
|
340
363
|
.describe(
|
|
341
|
-
"Which kind of yes: a grant past the persona or area, a credential's release, letting its installs run unasked, a secret sent past its host guard, or
|
|
364
|
+
"Which kind of yes: a grant past the persona or area, a credential's release, letting its installs run unasked, a secret sent past its host guard, allowing everything, or a credential gateway rule that stopped asking.",
|
|
342
365
|
),
|
|
343
|
-
what: z
|
|
366
|
+
what: z
|
|
367
|
+
.string()
|
|
368
|
+
.describe("The capability id, folder, shelf, released credential or secret it named, or the rule's identity; empty for installs and everything."),
|
|
369
|
+
capability: z.string().optional().describe("For a rule, the capability whose rule it is; a rule's identity alone does not say."),
|
|
344
370
|
});
|
|
345
371
|
export type GrantRevoke = z.infer<typeof GrantRevokeSchema>;
|
|
346
372
|
|
package/src/schemas/offload.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { RunnerCommandFrameSchema, RunnerCommandSchema } from "../protocol/doors/runner-protocol.js";
|
|
3
|
+
import { AgentHarnessSchema, AgentProviderSchema } from "./agent.js";
|
|
3
4
|
|
|
4
5
|
// OFFLOADED WORK, as the in-sandbox `offload-run` command and the editor see it (settings `offload`): a heavy line this
|
|
5
6
|
// sandbox hands a runner on one of the owner's machines instead of running it itself.
|
|
@@ -42,3 +43,78 @@ export type OffloadRecord = z.infer<typeof OffloadRecordSchema>;
|
|
|
42
43
|
// `offload.commands` keys it) and the pattern that sorts a line into it.
|
|
43
44
|
export const OffloadKindSchema = z.object({ id: z.string().min(1), pattern: z.string() });
|
|
44
45
|
export type OffloadKind = z.infer<typeof OffloadKindSchema>;
|
|
46
|
+
|
|
47
|
+
// NEW SESSIONS ACROSS THE OWNER'S MACHINES (settings `offload.sessions`, GET /offload/placement). One measure for every
|
|
48
|
+
// machine, this sandbox and each runner alike, and one judgement over it (runners/fleet-advice.ts), so the composer's
|
|
49
|
+
// suggestion, the placement menu and an automatic placement can never disagree about which machine has room.
|
|
50
|
+
|
|
51
|
+
// What happens to a new session while this machine is busy and another has room: `suggest` says so above the
|
|
52
|
+
// composer, `auto` starts it there unless the person keeps it here, `off` says nothing.
|
|
53
|
+
export const SessionsPlacementSchema = z.enum(["suggest", "auto", "off"]);
|
|
54
|
+
export type SessionsPlacement = z.infer<typeof SessionsPlacementSchema>;
|
|
55
|
+
|
|
56
|
+
// `calm` takes another agent without anyone noticing; `busy` takes one at a cost to the agents already running; `full`
|
|
57
|
+
// would hold or warn a person's next turn (the turn door's own figure).
|
|
58
|
+
export const MachineStandingSchema = z.enum(["calm", "busy", "full"]);
|
|
59
|
+
export type MachineStanding = z.infer<typeof MachineStandingSchema>;
|
|
60
|
+
|
|
61
|
+
// What made a machine busy or full: memory short of what an agent needs, memory stalling, CPUs saturated, or as many
|
|
62
|
+
// agents running as it has slots for.
|
|
63
|
+
export const MachineStrainSchema = z.enum(["memory", "memory-stall", "cpu", "agents"]);
|
|
64
|
+
export type MachineStrain = z.infer<typeof MachineStrainSchema>;
|
|
65
|
+
|
|
66
|
+
export const MachineLoadSchema = z.object({
|
|
67
|
+
standing: MachineStandingSchema,
|
|
68
|
+
// Why it is not calm, strongest first; empty when it is.
|
|
69
|
+
strain: z.array(MachineStrainSchema),
|
|
70
|
+
cpus: z.number().int().positive(),
|
|
71
|
+
// The one-minute load average over the cores: 1 is every core busy.
|
|
72
|
+
load: z.number().nonnegative(),
|
|
73
|
+
// What its resource budget has free, and the limit that is measured against; absent where nothing measures it.
|
|
74
|
+
memoryFreeMb: z.number().int().nonnegative().optional(),
|
|
75
|
+
memoryLimitMb: z.number().int().positive().optional(),
|
|
76
|
+
// Conversations running a turn there now, and how many it holds at once (the fan-out scheduler's own slots).
|
|
77
|
+
agents: z.number().int().nonnegative(),
|
|
78
|
+
slots: z.number().int().positive(),
|
|
79
|
+
// The share of the machine still free by its tightest measure (slots, memory, CPU), so two machines of different
|
|
80
|
+
// sizes compare: 0 is none, 1 is idle.
|
|
81
|
+
headroom: z.number().min(0).max(1),
|
|
82
|
+
});
|
|
83
|
+
export type MachineLoad = z.infer<typeof MachineLoadSchema>;
|
|
84
|
+
|
|
85
|
+
// Why a new session could not start on a runner right now: the machine is asleep, it has never said how big it is, the
|
|
86
|
+
// draft's model signs in with a credential that does not travel, the privacy shield keeps it here, or the person is
|
|
87
|
+
// limited to areas a runner cannot fence.
|
|
88
|
+
export const FleetUnfitSchema = z.enum(["offline", "unmeasured", "credentials", "privacy", "areas"]);
|
|
89
|
+
export type FleetUnfit = z.infer<typeof FleetUnfitSchema>;
|
|
90
|
+
|
|
91
|
+
export const FleetMachineSchema = z.object({
|
|
92
|
+
runner: z.string().min(1),
|
|
93
|
+
// The machine the runner sits on, the word the owner knows it by.
|
|
94
|
+
name: z.string().min(1),
|
|
95
|
+
online: z.boolean(),
|
|
96
|
+
// Absent while it is offline or has never been measured.
|
|
97
|
+
load: MachineLoadSchema.optional(),
|
|
98
|
+
// Absent when a new session could start there now.
|
|
99
|
+
unfit: FleetUnfitSchema.optional(),
|
|
100
|
+
});
|
|
101
|
+
export type FleetMachine = z.infer<typeof FleetMachineSchema>;
|
|
102
|
+
|
|
103
|
+
export const FleetAdviceInputSchema = z.object({
|
|
104
|
+
// The draft's provider and harness, which decide whether its credential can reach a runner; absent reads as the
|
|
105
|
+
// sandbox's defaults.
|
|
106
|
+
agent: AgentProviderSchema.optional(),
|
|
107
|
+
harness: AgentHarnessSchema.optional(),
|
|
108
|
+
// The draft's conversation, which the privacy shield may trust on its own.
|
|
109
|
+
conversationId: z.string().min(1).optional(),
|
|
110
|
+
});
|
|
111
|
+
export type FleetAdviceInput = z.infer<typeof FleetAdviceInputSchema>;
|
|
112
|
+
|
|
113
|
+
export const FleetAdviceSchema = z.object({
|
|
114
|
+
mode: SessionsPlacementSchema,
|
|
115
|
+
here: MachineLoadSchema,
|
|
116
|
+
machines: z.array(FleetMachineSchema),
|
|
117
|
+
// Where a new session should start instead of here: absent while here is calm, or when no machine is better off.
|
|
118
|
+
suggestion: z.object({ runner: z.string().min(1), name: z.string().min(1) }).optional(),
|
|
119
|
+
});
|
|
120
|
+
export type FleetAdvice = z.infer<typeof FleetAdviceSchema>;
|
package/src/schemas/privacy.ts
CHANGED
|
@@ -87,7 +87,8 @@ export const trustedInConversation = (
|
|
|
87
87
|
policy: Pick<PrivacyShieldPolicy, "conversations">,
|
|
88
88
|
provider: string,
|
|
89
89
|
conversationId: string | undefined,
|
|
90
|
-
): boolean =>
|
|
90
|
+
): boolean =>
|
|
91
|
+
conversationId !== undefined && policy.conversations.some((entry) => entry.conversationId === conversationId && entry.provider === provider);
|
|
91
92
|
|
|
92
93
|
// The policy with that grant made or taken back. A full list drops its oldest grant to make room, since the newest is
|
|
93
94
|
// the one somebody is waiting on.
|
|
@@ -104,7 +105,11 @@ export const DEFAULT_PRIVACY_SHIELD: PrivacyShieldPolicy = PrivacyShieldPolicySc
|
|
|
104
105
|
export const PrivacyProviderSchema = z.object({
|
|
105
106
|
id: z.string().describe("Provider id, as the trusted list names it."),
|
|
106
107
|
label: z.string(),
|
|
107
|
-
shieldable: z
|
|
108
|
+
shieldable: z
|
|
109
|
+
.boolean()
|
|
110
|
+
.describe(
|
|
111
|
+
"The shield can read what its runtime sends by content (through the gateway or the runtime's hooks); one it cannot is refused while the shield is on, unless trusted.",
|
|
112
|
+
),
|
|
108
113
|
local: z.boolean().describe("It runs on this machine, so it is trusted whatever the list says."),
|
|
109
114
|
});
|
|
110
115
|
export type PrivacyProvider = z.infer<typeof PrivacyProviderSchema>;
|
|
@@ -140,6 +145,10 @@ export const PrivacyReplacementSchema = z.object({
|
|
|
140
145
|
.string()
|
|
141
146
|
.describe("The masked text around the token as it left this machine (watching: as it would have). Tokens only, never a value."),
|
|
142
147
|
image: z.boolean().optional().describe("Found in an image's text, so the token was painted over the picture rather than written."),
|
|
148
|
+
imageId: z
|
|
149
|
+
.string()
|
|
150
|
+
.optional()
|
|
151
|
+
.describe("The image it was painted over, as privacy.image reads it back: the picture before and after masking, while it is still kept."),
|
|
143
152
|
});
|
|
144
153
|
export type PrivacyReplacement = z.infer<typeof PrivacyReplacementSchema>;
|
|
145
154
|
|
|
@@ -162,6 +171,17 @@ export const PrivacyLedgerEntrySchema = z.object({
|
|
|
162
171
|
});
|
|
163
172
|
export type PrivacyLedgerEntry = z.infer<typeof PrivacyLedgerEntrySchema>;
|
|
164
173
|
|
|
174
|
+
// One image the shield painted over, as it was and as it left: the owner peeking at what the provider saw. The picture
|
|
175
|
+
// before holds the personal data, so it is kept beside the vault, off the workspace, and only the owner may read it.
|
|
176
|
+
export const PRIVACY_IMAGE_ID = /^[A-Za-z0-9_-]{16,64}$/u;
|
|
177
|
+
export const PrivacyImageSchema = z.object({ mediaType: z.string(), data: z.string().describe("Base64.") });
|
|
178
|
+
export const PrivacyImagePairSchema = z.object({
|
|
179
|
+
at: z.string().describe("When it was first painted, as an ISO timestamp."),
|
|
180
|
+
before: PrivacyImageSchema,
|
|
181
|
+
after: PrivacyImageSchema,
|
|
182
|
+
});
|
|
183
|
+
export type PrivacyImagePair = z.infer<typeof PrivacyImagePairSchema>;
|
|
184
|
+
|
|
165
185
|
// Tokens read back to their values, for the owner checking what the shield masked; a token this vault never gave out is
|
|
166
186
|
// left out of the answer.
|
|
167
187
|
export const PRIVACY_REVEAL_MAX = 100;
|
|
@@ -205,7 +225,11 @@ export const PrivacyNameWordSchema = z.object({
|
|
|
205
225
|
firstName: z.boolean().describe("A listed first name, in this form or as an inflection of one."),
|
|
206
226
|
surname: z.boolean().describe("A listed surname, in this form or as an inflection of one."),
|
|
207
227
|
surnameForm: z.boolean().describe("Shaped like a Polish surname (-ski, -cki, -wicz…), listed or not."),
|
|
208
|
-
ambiguous: z
|
|
228
|
+
ambiguous: z
|
|
229
|
+
.boolean()
|
|
230
|
+
.describe(
|
|
231
|
+
"Also an ordinary word in some language, so found only beside other evidence (a title, a name field) and no surname to pair a first name with.",
|
|
232
|
+
),
|
|
209
233
|
never: z.boolean().describe("Never taken as part of a name (a title, an institution, a function word)."),
|
|
210
234
|
});
|
|
211
235
|
export type PrivacyNameWord = z.infer<typeof PrivacyNameWordSchema>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ProjectAnswer, ProjectAnswerSchema } from "./project-bring-back.js";
|
|
2
|
+
|
|
3
|
+
test("reads the agent's change listing as the desktop app relays it", () => {
|
|
4
|
+
const detail: ProjectAnswer = {
|
|
5
|
+
kind: "changes",
|
|
6
|
+
result: { ok: true, pairing: "p1", direction: "to-sandbox", changes: [{ path: "a.ts", kind: "modified", size: 3, conflict: true }], truncated: true },
|
|
7
|
+
};
|
|
8
|
+
expect(ProjectAnswerSchema.parse(detail)).toEqual(detail);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("a restore's skipped entries are held to their shape, and a failure carries the agent's sentence", () => {
|
|
12
|
+
expect(ProjectAnswerSchema.parse({ kind: "restored", result: { ok: true, restored: 2, skipped: [{ path: "x", reason: "changed" }] } })).toMatchObject({ result: { restored: 2 } });
|
|
13
|
+
expect(ProjectAnswerSchema.parse({ kind: "brought-back", result: { ok: false, error: "conflict" } })).toEqual({ kind: "brought-back", result: { ok: false, error: "conflict" } });
|
|
14
|
+
expect(ProjectAnswerSchema.safeParse({ kind: "changes", result: { note: "x" } }).success).toBe(false);
|
|
15
|
+
});
|