@intentic/sandbox-contract 1.304.0 → 1.305.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/contracts/agent.contract.d.ts +195 -41
- package/dist/contracts/agent.contract.d.ts.map +1 -1
- package/dist/contracts/agent.contract.js +8 -8
- package/dist/contracts/agent.contract.js.map +1 -1
- package/dist/contracts/agents.contract.d.ts +203 -3
- package/dist/contracts/agents.contract.d.ts.map +1 -1
- package/dist/contracts/agents.contract.js +30 -2
- package/dist/contracts/agents.contract.js.map +1 -1
- package/dist/contracts/capabilities.contract.d.ts +18 -5
- package/dist/contracts/capabilities.contract.d.ts.map +1 -1
- package/dist/contracts/capabilities.contract.js +3 -3
- package/dist/contracts/capabilities.contract.js.map +1 -1
- package/dist/contracts/{host.contract.d.ts → device.contract.d.ts} +75 -2
- package/dist/contracts/device.contract.d.ts.map +1 -0
- package/dist/contracts/device.contract.js +17 -0
- package/dist/contracts/device.contract.js.map +1 -0
- package/dist/contracts/extensions.contract.d.ts +2 -2
- package/dist/contracts/git.contract.d.ts +24 -0
- package/dist/contracts/git.contract.d.ts.map +1 -1
- package/dist/contracts/panels.contract.d.ts +1 -1
- package/dist/contracts/personas.contract.d.ts +0 -9
- package/dist/contracts/personas.contract.d.ts.map +1 -1
- package/dist/contracts/personas.contract.js +1 -10
- package/dist/contracts/personas.contract.js.map +1 -1
- package/dist/contracts/runner.contract.d.ts +138 -98
- package/dist/contracts/runner.contract.d.ts.map +1 -1
- package/dist/contracts/runner.contract.js +1 -0
- package/dist/contracts/runner.contract.js.map +1 -1
- package/dist/contracts/secrets.contract.js +2 -2
- package/dist/contracts/secrets.contract.js.map +1 -1
- package/dist/contracts/sessions.contract.d.ts +23 -3
- package/dist/contracts/sessions.contract.d.ts.map +1 -1
- package/dist/contracts/settings.contract.d.ts +0 -23
- package/dist/contracts/settings.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.d.ts +106 -8
- package/dist/contracts/system.contract.d.ts.map +1 -1
- package/dist/contracts/system.contract.js +11 -2
- package/dist/contracts/system.contract.js.map +1 -1
- package/dist/events/agent-events.d.ts +85 -10
- package/dist/events/agent-events.d.ts.map +1 -1
- package/dist/events/agent-events.js +26 -10
- package/dist/events/agent-events.js.map +1 -1
- package/dist/events/agent-words.d.ts +20 -0
- package/dist/events/agent-words.d.ts.map +1 -0
- package/dist/events/agent-words.js +82 -0
- package/dist/events/agent-words.js.map +1 -0
- package/dist/events/{cards.d.ts → requests.d.ts} +18 -18
- package/dist/events/requests.d.ts.map +1 -0
- package/dist/events/{cards.js → requests.js} +26 -26
- package/dist/events/requests.js.map +1 -0
- package/dist/events/resume.d.ts +1 -0
- package/dist/events/resume.d.ts.map +1 -1
- package/dist/events/resume.js +2 -0
- package/dist/events/resume.js.map +1 -1
- package/dist/events/system-events.d.ts +19 -3
- package/dist/events/system-events.d.ts.map +1 -1
- package/dist/events/transcript.d.ts +165 -25
- package/dist/events/transcript.d.ts.map +1 -1
- package/dist/events/transcript.js +34 -15
- package/dist/events/transcript.js.map +1 -1
- package/dist/events/watch-wake.d.ts.map +1 -1
- package/dist/events/watch-wake.js +4 -0
- package/dist/events/watch-wake.js.map +1 -1
- package/dist/index.d.ts +581 -99
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/models/model-offer.d.ts +2 -1
- package/dist/models/model-offer.d.ts.map +1 -1
- package/dist/models/model-offer.js +5 -4
- package/dist/models/model-offer.js.map +1 -1
- package/dist/models/model-roles.d.ts +2 -9
- package/dist/models/model-roles.d.ts.map +1 -1
- package/dist/models/model-roles.js +2 -9
- package/dist/models/model-roles.js.map +1 -1
- package/dist/policy/command-classes.d.ts +1 -0
- package/dist/policy/command-classes.d.ts.map +1 -1
- package/dist/policy/command-classes.js +8 -0
- package/dist/policy/command-classes.js.map +1 -1
- package/dist/policy/request-status.d.ts +5 -0
- package/dist/policy/request-status.d.ts.map +1 -0
- package/dist/policy/{card-status.js → request-status.js} +3 -3
- package/dist/policy/request-status.js.map +1 -0
- package/dist/policy/safety-policy.d.ts +1 -1
- package/dist/policy/safety-policy.d.ts.map +1 -1
- package/dist/policy/safety-policy.js +1 -0
- package/dist/policy/safety-policy.js.map +1 -1
- package/dist/policy/turned-away.d.ts +2 -0
- package/dist/policy/turned-away.d.ts.map +1 -0
- package/dist/policy/turned-away.js +13 -0
- package/dist/policy/turned-away.js.map +1 -0
- package/dist/protocol/peer-dial.d.ts +9 -0
- package/dist/protocol/peer-dial.d.ts.map +1 -1
- package/dist/protocol/peer-dial.js +12 -4
- package/dist/protocol/peer-dial.js.map +1 -1
- package/dist/protocol/peer-mcp-server.d.ts +1 -0
- package/dist/protocol/peer-mcp-server.d.ts.map +1 -1
- package/dist/protocol/peer-mcp-server.js +14 -1
- package/dist/protocol/peer-mcp-server.js.map +1 -1
- package/dist/schemas/agent.d.ts +2 -1
- package/dist/schemas/agent.d.ts.map +1 -1
- package/dist/schemas/agent.js +7 -0
- package/dist/schemas/agent.js.map +1 -1
- package/dist/schemas/agents.d.ts +26 -0
- package/dist/schemas/agents.d.ts.map +1 -1
- package/dist/schemas/agents.js +14 -0
- package/dist/schemas/agents.js.map +1 -1
- package/dist/schemas/automations.d.ts +8 -0
- package/dist/schemas/automations.d.ts.map +1 -1
- package/dist/schemas/capabilities.d.ts +25 -12
- package/dist/schemas/capabilities.d.ts.map +1 -1
- package/dist/schemas/capabilities.js +11 -6
- package/dist/schemas/capabilities.js.map +1 -1
- package/dist/schemas/chat-route.d.ts +49 -0
- package/dist/schemas/chat-route.d.ts.map +1 -0
- package/dist/schemas/{model-route.js → chat-route.js} +17 -5
- package/dist/schemas/chat-route.js.map +1 -0
- package/dist/schemas/context-trim.d.ts +9 -0
- package/dist/schemas/context-trim.d.ts.map +1 -0
- package/dist/schemas/context-trim.js +18 -0
- package/dist/schemas/context-trim.js.map +1 -0
- package/dist/schemas/devices.d.ts +21 -8
- package/dist/schemas/devices.d.ts.map +1 -1
- package/dist/schemas/devices.js +8 -9
- package/dist/schemas/devices.js.map +1 -1
- package/dist/schemas/extension-updates.d.ts +4 -4
- package/dist/schemas/extension-updates.js +1 -1
- package/dist/schemas/extension-updates.js.map +1 -1
- package/dist/schemas/git/git.d.ts +83 -0
- package/dist/schemas/git/git.d.ts.map +1 -1
- package/dist/schemas/git/git.js +40 -0
- package/dist/schemas/git/git.js.map +1 -1
- package/dist/schemas/hosts.d.ts +36 -5
- package/dist/schemas/hosts.d.ts.map +1 -1
- package/dist/schemas/hosts.js +7 -5
- package/dist/schemas/hosts.js.map +1 -1
- package/dist/schemas/metrics.d.ts +108 -0
- package/dist/schemas/metrics.d.ts.map +1 -0
- package/dist/schemas/metrics.js +77 -0
- package/dist/schemas/metrics.js.map +1 -0
- package/dist/schemas/panels.d.ts +2 -2
- package/dist/schemas/panels.js +1 -1
- package/dist/schemas/panels.js.map +1 -1
- package/dist/schemas/personas.d.ts +1 -13
- package/dist/schemas/personas.d.ts.map +1 -1
- package/dist/schemas/personas.js +3 -20
- package/dist/schemas/personas.js.map +1 -1
- package/dist/schemas/providers/plan-limits.d.ts +1 -0
- package/dist/schemas/providers/plan-limits.d.ts.map +1 -1
- package/dist/schemas/providers/plan-limits.js +6 -0
- package/dist/schemas/providers/plan-limits.js.map +1 -1
- package/dist/schemas/secrets.d.ts +1 -1
- package/dist/schemas/secrets.d.ts.map +1 -1
- package/dist/schemas/secrets.js +1 -1
- package/dist/schemas/secrets.js.map +1 -1
- package/dist/schemas/settings.d.ts +0 -42
- package/dist/schemas/settings.d.ts.map +1 -1
- package/dist/schemas/settings.js +1 -16
- package/dist/schemas/settings.js.map +1 -1
- package/dist/schemas/shared.d.ts +2 -2
- package/dist/schemas/shared.d.ts.map +1 -1
- package/dist/schemas/shared.js +3 -3
- package/dist/schemas/shared.js.map +1 -1
- package/dist/schemas/system-prompt.d.ts +104 -0
- package/dist/schemas/system-prompt.d.ts.map +1 -0
- package/dist/schemas/system-prompt.js +25 -0
- package/dist/schemas/system-prompt.js.map +1 -0
- package/dist/schemas/workflows.d.ts +2 -2
- package/dist/schemas/workflows.d.ts.map +1 -1
- package/dist/schemas/workflows.js +2 -2
- package/dist/schemas/workflows.js.map +1 -1
- package/dist/state/definition.d.ts +18 -10
- package/dist/state/definition.d.ts.map +1 -1
- package/dist/state/fix-attempt-plan.d.ts +4 -0
- package/dist/state/fix-attempt-plan.d.ts.map +1 -1
- package/dist/state/fix-attempt-plan.js +3 -2
- package/dist/state/fix-attempt-plan.js.map +1 -1
- package/dist/state/fix-stance.d.ts +1 -0
- package/dist/state/fix-stance.d.ts.map +1 -1
- package/dist/state/fix-stance.js +11 -0
- package/dist/state/fix-stance.js.map +1 -1
- package/dist/state/history-state.d.ts.map +1 -1
- package/dist/state/history-state.js +8 -1
- package/dist/state/history-state.js.map +1 -1
- package/dist/state/runtime-state.d.ts +0 -1
- package/dist/state/runtime-state.d.ts.map +1 -1
- package/dist/state/runtime-state.js +0 -1
- package/dist/state/runtime-state.js.map +1 -1
- package/dist/state/workspace-state.d.ts +4 -4
- package/dist/state/workspace-state.d.ts.map +1 -1
- package/dist/state/workspace-state.js +4 -4
- package/dist/state/workspace-state.js.map +1 -1
- package/dist/text/documents.d.ts +2 -2
- package/dist/text/documents.d.ts.map +1 -1
- package/dist/text/documents.js.map +1 -1
- package/dist/text/mentions.d.ts +1 -0
- package/dist/text/mentions.d.ts.map +1 -1
- package/dist/text/mentions.js +5 -1
- package/dist/text/mentions.js.map +1 -1
- package/dist/text/transcript-fold.d.ts +8 -3
- package/dist/text/transcript-fold.d.ts.map +1 -1
- package/dist/text/transcript-fold.js +83 -28
- package/dist/text/transcript-fold.js.map +1 -1
- package/package.json +9 -9
- package/src/chores/chores.test.ts +1 -1
- package/src/chores/digest.test.ts +1 -1
- package/src/chores/probes.test.ts +5 -2
- package/src/chores/stack.test.ts +1 -1
- package/src/chores/verdict.test.ts +6 -5
- package/src/contracts/agent.contract.ts +10 -9
- package/src/contracts/agents.contract.ts +35 -2
- package/src/contracts/capabilities.contract.ts +3 -3
- package/src/contracts/{host.contract.ts → device.contract.ts} +8 -6
- package/src/contracts/personas.contract.ts +1 -13
- package/src/contracts/runner.contract.ts +3 -2
- package/src/contracts/secrets.contract.ts +2 -2
- package/src/contracts/system.contract.ts +13 -2
- package/src/contracts/webext.contract.ts +1 -1
- package/src/events/agent-events.ts +37 -14
- package/src/events/agent-words.test.ts +68 -0
- package/src/events/agent-words.ts +124 -0
- package/src/events/{cards.ts → requests.ts} +44 -44
- package/src/events/resume.test.ts +8 -1
- package/src/events/resume.ts +4 -0
- package/src/events/transcript.ts +51 -22
- package/src/events/watch-wake.test.ts +3 -2
- package/src/events/watch-wake.ts +4 -0
- package/src/ids/conversation-ids.test.ts +3 -3
- package/src/ids/hostnames.test.ts +1 -1
- package/src/ids/share-paths.test.ts +1 -1
- package/src/ids/tunnel-ids.test.ts +1 -1
- package/src/index.ts +9 -4
- package/src/models/agent-catalog.test.ts +1 -1
- package/src/models/capability-ledger.test.ts +1 -1
- package/src/models/model-offer.test.ts +9 -2
- package/src/models/model-offer.ts +10 -7
- package/src/models/model-order.test.ts +1 -1
- package/src/models/model-pins.test.ts +1 -1
- package/src/models/model-roles.test.ts +1 -1
- package/src/models/model-roles.ts +6 -12
- package/src/models/plan-pools.test.ts +1 -1
- package/src/models/provider-specs.test.ts +3 -3
- package/src/policy/batch-runs.test.ts +1 -1
- package/src/policy/command-classes.test.ts +37 -2
- package/src/policy/command-classes.ts +16 -0
- package/src/policy/credential-material.test.ts +6 -10
- package/src/policy/fence-paths.test.ts +1 -1
- package/src/policy/overlay-lint.test.ts +1 -1
- package/src/policy/owner-ticket.test.ts +1 -1
- package/src/policy/persona-home.test.ts +1 -1
- package/src/policy/{card-status.ts → request-status.ts} +6 -6
- package/src/policy/safety-policy.test.ts +1 -1
- package/src/policy/safety-policy.ts +1 -0
- package/src/policy/search-globs.test.ts +1 -1
- package/src/policy/turned-away.ts +18 -0
- package/src/protocol/container-requirements.test.ts +1 -1
- package/src/protocol/ingress-contract.test.ts +8 -2
- package/src/protocol/ingress-protocol.test.ts +5 -4
- package/src/protocol/peer-dial.test.ts +111 -41
- package/src/protocol/peer-dial.ts +39 -4
- package/src/protocol/peer-mcp-server.test.ts +37 -7
- package/src/protocol/peer-mcp-server.ts +22 -1
- package/src/protocol/routes.test.ts +4 -2
- package/src/schemas/agent.ts +13 -0
- package/src/schemas/agents.ts +25 -0
- package/src/schemas/areas.test.ts +2 -8
- package/src/schemas/automations.ts +5 -5
- package/src/schemas/capabilities.ts +20 -9
- package/src/schemas/chat-route.ts +61 -0
- package/src/schemas/context-trim.test.ts +38 -0
- package/src/schemas/context-trim.ts +29 -0
- package/src/schemas/dev-rebuild-log.test.ts +1 -1
- package/src/schemas/devices.ts +25 -22
- package/src/schemas/extension-updates.ts +1 -1
- package/src/schemas/git/git.test.ts +11 -0
- package/src/schemas/git/git.ts +66 -0
- package/src/schemas/hosts.ts +15 -8
- package/src/schemas/issues.ts +1 -1
- package/src/schemas/metrics.ts +107 -0
- package/src/schemas/panels.ts +2 -2
- package/src/schemas/personas.ts +5 -28
- package/src/schemas/providers/plan-limits.ts +9 -0
- package/src/schemas/secrets.ts +1 -1
- package/src/schemas/settings.test.ts +1 -1
- package/src/schemas/settings.ts +9 -36
- package/src/schemas/shared.ts +6 -6
- package/src/schemas/system-prompt.ts +41 -0
- package/src/schemas/version-seam.test.ts +1 -1
- package/src/schemas/webext.ts +1 -1
- package/src/schemas/workflows.ts +4 -4
- package/src/state/contract-lock.test.ts +15 -11
- package/src/state/fix-attempt-plan.test.ts +10 -1
- package/src/state/fix-attempt-plan.ts +6 -2
- package/src/state/fix-stance.test.ts +10 -1
- package/src/state/fix-stance.ts +13 -0
- package/src/state/history-state.ts +17 -1
- package/src/state/runtime-state.test.ts +3 -9
- package/src/state/runtime-state.ts +0 -4
- package/src/state/versions.test.ts +1 -1
- package/src/state/workspace-state.test.ts +12 -7
- package/src/state/workspace-state.ts +4 -4
- package/src/text/archives.test.ts +1 -1
- package/src/text/documents.test.ts +1 -1
- package/src/text/documents.ts +2 -2
- package/src/text/embed.test.ts +17 -6
- package/src/text/emoji.test.ts +1 -1
- package/src/text/mentions.test.ts +20 -2
- package/src/text/mentions.ts +10 -1
- package/src/text/path-refs.test.ts +1 -1
- package/src/text/title.test.ts +1 -1
- package/src/text/transcript-fold.test.ts +183 -1
- package/src/text/transcript-fold.ts +146 -39
- package/src/text/whisper.test.ts +1 -1
- package/src/text/workflow-faults.test.ts +1 -1
- package/src/time/zone.test.ts +6 -6
- package/dist/contracts/host.contract.d.ts.map +0 -1
- package/dist/contracts/host.contract.js +0 -16
- package/dist/contracts/host.contract.js.map +0 -1
- package/dist/events/cards.d.ts.map +0 -1
- package/dist/events/cards.js.map +0 -1
- package/dist/policy/card-status.d.ts +0 -5
- package/dist/policy/card-status.d.ts.map +0 -1
- package/dist/policy/card-status.js.map +0 -1
- package/dist/schemas/model-route.d.ts +0 -27
- package/dist/schemas/model-route.d.ts.map +0 -1
- package/dist/schemas/model-route.js.map +0 -1
- package/src/schemas/model-route.ts +0 -43
package/src/schemas/settings.ts
CHANGED
|
@@ -13,10 +13,6 @@ export const SystemPromptModeSchema = z.enum(["intentic", "claude", "custom"]);
|
|
|
13
13
|
export type SystemPromptMode = z.infer<typeof SystemPromptModeSchema>;
|
|
14
14
|
// Excludes "custom": there is nothing to fetch, it's whatever the owner already typed into the settings field.
|
|
15
15
|
export const BuiltinPromptSchema = z.object({ base: z.enum(["intentic", "claude"]) });
|
|
16
|
-
// Declared out here because the daemon branches on it in three places (whether to wire the hook, consult the successor
|
|
17
|
-
// list, what the notice says) and the browser branches on it too.
|
|
18
|
-
export const DependencyFreshnessSchema = z.enum(["off", "versions", "full"]);
|
|
19
|
-
export type DependencyFreshness = z.infer<typeof DependencyFreshnessSchema>;
|
|
20
16
|
// Rules: "at this moment, if this is true, do this" — one table replacing three settings that were the same idea built
|
|
21
17
|
// three ways; a fourth is now a row here, not a release. Moments are named to match `WorkspaceEventKind`, so folding
|
|
22
18
|
// chores in later won't rename what users wrote.
|
|
@@ -225,14 +221,15 @@ export const SandboxSettingsSchema = z.object({
|
|
|
225
221
|
"Keep the instructions identical between turns so the provider can cache them, moving anything that varies into the message instead. Cheaper, at the cost of some flexibility.",
|
|
226
222
|
),
|
|
227
223
|
skills: z.array(z.string()).default(["lsp", "fileq"]).describe("Which built-in tools are switched on. A skill of your own is not listed here: it is on while the agent's copy of it exists."),
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
//
|
|
224
|
+
// One half of the reading a new chat gets between send and its first turn (chat-router.ts): with this on, the
|
|
225
|
+
// message and one line per card are put to the same model that chooses what the chat runs on, in the same call.
|
|
226
|
+
// Never asked of a draft: only a sent message is a finished one. Attended chats only: routing onto a card would
|
|
227
|
+
// grant an unwatched wake accounts nobody named for it.
|
|
231
228
|
personaRouting: z
|
|
232
229
|
.boolean()
|
|
233
230
|
.default(true)
|
|
234
231
|
.describe(
|
|
235
|
-
"Whether a new chat is matched to one of your personas from its first message.
|
|
232
|
+
"Whether a new chat is matched to one of your personas from its first message. It is read once the message is sent, in the same single call that chooses what the chat runs on (the New chat routing job under Models), and the chat says in its own transcript which persona it landed on. Never applies to unwatched runs, which name their persona themselves.",
|
|
236
233
|
),
|
|
237
234
|
hashlineEdits: z
|
|
238
235
|
.boolean()
|
|
@@ -240,7 +237,8 @@ export const SandboxSettingsSchema = z.object({
|
|
|
240
237
|
.describe(
|
|
241
238
|
"Have the agent edit files by line number rather than by quoting the text it wants replaced. Cheaper on large files, and less forgiving of a stale read.",
|
|
242
239
|
),
|
|
243
|
-
// intentic:
|
|
240
|
+
// intentic: Claude Code's preset, read the same way, with what this harness does not want cut out (intentic-prompt.ts).
|
|
241
|
+
// Default.
|
|
244
242
|
// claude: Claude Code's preset, read live from the installed CLI, not a stored copy — tracks whatever prompt ships
|
|
245
243
|
// with it.
|
|
246
244
|
// custom: `systemPrompt` below, and nothing else.
|
|
@@ -321,14 +319,6 @@ export const SandboxSettingsSchema = z.object({
|
|
|
321
319
|
.describe(
|
|
322
320
|
"Keep an up-to-date markdown rendering of every document, image and audio file in the workspace, made in the background as files land, so the agent reads a pre-derived text instead of paying to parse the file mid-task. Costs background CPU on a document-heavy workspace, so it is a switch rather than a default.",
|
|
323
321
|
),
|
|
324
|
-
// versions: a measurement only (the registry's own newest release).
|
|
325
|
-
// full: also the name of a successor, but only where the registry itself corroborates it (deprecated, or nothing
|
|
326
|
-
// published in 18 months) — curation supplies the name, measurement supplies the reason.
|
|
327
|
-
// Informs, never blocks: matching a version the workspace already pins is usually correct, and a gate would fight
|
|
328
|
-
// legitimate work more than it caught mistakes.
|
|
329
|
-
dependencyFreshness: DependencyFreshnessSchema.default("off").describe(
|
|
330
|
-
"Whether a version the agent is about to pin is checked against the package's own registry first. Facts only, or facts plus the name of a maintained replacement where the registry agrees the current choice has been abandoned. It tells the agent and lets it decide rather than refusing, because matching a version your project already uses is usually the right answer and a gate would fight it.",
|
|
331
|
-
),
|
|
332
322
|
outputCleaners: z
|
|
333
323
|
.string()
|
|
334
324
|
.default("")
|
|
@@ -349,7 +339,7 @@ export const SandboxSettingsSchema = z.object({
|
|
|
349
339
|
.describe(
|
|
350
340
|
"Which models do which job, one ordered list per job: commit messages, session titles, the safety judge, pipeline fixes, and every other place this sandbox picks a model for you. Tried in order, so one spent account does not take a job down. Nothing is chosen for you: a one-shot job with no list does not run, and a whole session with no list opens on whatever your own chat is set to.",
|
|
351
341
|
),
|
|
352
|
-
// Folded into the
|
|
342
|
+
// Folded into the router's prompt (agent/prompt/chat-router.ts), never into a turn's own. Capped small on
|
|
353
343
|
// purpose: the reading runs under a 5s deadline, and this text is paid for on every chat that opens on Auto.
|
|
354
344
|
autoModelGuidance: z
|
|
355
345
|
.string()
|
|
@@ -479,7 +469,7 @@ export const TimezoneStateSchema = z.object({
|
|
|
479
469
|
});
|
|
480
470
|
export type TimezoneState = z.infer<typeof TimezoneStateSchema>;
|
|
481
471
|
// Read live from the installed CLI (preset-prompt.ts), not a stored transcription. `version` is the CLI build it came
|
|
482
|
-
// from, so a fork from an older build reads as a snapshot
|
|
472
|
+
// from, so a fork from an older build reads as a snapshot.
|
|
483
473
|
export const BuiltinPromptTextSchema = z.object({ text: z.string(), version: z.string() });
|
|
484
474
|
export type BuiltinPromptText = z.infer<typeof BuiltinPromptTextSchema>;
|
|
485
475
|
// What each token-reduction mechanism actually saved. Input-side: both sides of the comparison come off the same
|
|
@@ -551,22 +541,6 @@ export const TurnExperimentSchema = z.object({
|
|
|
551
541
|
cohort: z.string().optional(),
|
|
552
542
|
});
|
|
553
543
|
export type TurnExperiment = z.infer<typeof TurnExperimentSchema>;
|
|
554
|
-
// One dependency version or library improvement suggested or pinned.
|
|
555
|
-
export const DependencyImprovementSchema = z.object({
|
|
556
|
-
prevented: z.string(),
|
|
557
|
-
chosen: z.string(),
|
|
558
|
-
reason: z.string(),
|
|
559
|
-
at: z.number().optional(),
|
|
560
|
-
});
|
|
561
|
-
export type DependencyImprovement = z.infer<typeof DependencyImprovementSchema>;
|
|
562
|
-
// Rollup of registry freshness interventions over the queried day window.
|
|
563
|
-
export const DependencySavingsSchema = z.object({
|
|
564
|
-
checked: z.number(),
|
|
565
|
-
improved: z.number(),
|
|
566
|
-
recent: z.array(DependencyImprovementSchema),
|
|
567
|
-
updatedAt: z.number().optional(),
|
|
568
|
-
});
|
|
569
|
-
export type DependencySavings = z.infer<typeof DependencySavingsSchema>;
|
|
570
544
|
// What the settings row can say about the brief without opening it: whether there is one, how much of it this budget
|
|
571
545
|
// reaches, and whether anything is scheduled to rewrite it. Read off the file and the automation store, never stored.
|
|
572
546
|
export const FieldNotesStatusSchema = z.object({
|
|
@@ -595,7 +569,6 @@ export const SavingsReportSchema = z.object({
|
|
|
595
569
|
// Same absence rule as `search`: not measured, never zero.
|
|
596
570
|
map: TurnExperimentSchema.optional(),
|
|
597
571
|
notes: TurnExperimentSchema.optional(),
|
|
598
|
-
dependencies: DependencySavingsSchema.optional(),
|
|
599
572
|
});
|
|
600
573
|
export type SavingsReport = z.infer<typeof SavingsReportSchema>;
|
|
601
574
|
|
package/src/schemas/shared.ts
CHANGED
|
@@ -8,18 +8,18 @@ export const OkSchema = object({
|
|
|
8
8
|
ok: literal(true)
|
|
9
9
|
.describe("Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."),
|
|
10
10
|
});
|
|
11
|
-
// Trust tiers, ordered low to high:
|
|
11
|
+
// Trust tiers, ordered low to high: guest talks to the persona cards it was handed and sees nothing else, viewer
|
|
12
12
|
// watches, collaborator's outward actions become requests, writer changes files inside the areas it holds and ships
|
|
13
13
|
// none of them, maintainer holds the owner's authority but is revocable, owner is the one bound identity and not a
|
|
14
14
|
// grant.
|
|
15
|
-
export const MemberRoleSchema = zEnum(["
|
|
15
|
+
export const MemberRoleSchema = zEnum(["guest", "viewer", "collaborator", "writer", "maintainer", "owner"]);
|
|
16
16
|
export type MemberRole = z.infer<typeof MemberRoleSchema>;
|
|
17
17
|
// Roles an invite can grant: everything but owner, which binds at first sign-in and is never granted.
|
|
18
|
-
export const GrantedRoleSchema = zEnum(["
|
|
18
|
+
export const GrantedRoleSchema = zEnum(["guest", "viewer", "collaborator", "writer", "maintainer"]);
|
|
19
19
|
export type GrantedRole = z.infer<typeof GrantedRoleSchema>;
|
|
20
|
-
// Single source for role order; every surface that gates on a role reads this ranking. A
|
|
21
|
-
// floor: its routes are an allowlist of its own (auth/role-floor.ts
|
|
22
|
-
const MEMBER_ROLE_RANK: Record<MemberRole, number> = {
|
|
20
|
+
// Single source for role order; every surface that gates on a role reads this ranking. A guest is below every
|
|
21
|
+
// floor: its routes are an allowlist of its own (auth/role-floor.ts guestReach), never a floor it can clear.
|
|
22
|
+
const MEMBER_ROLE_RANK: Record<MemberRole, number> = { guest: 0, viewer: 1, collaborator: 2, writer: 3, maintainer: 4, owner: 5 };
|
|
23
23
|
export const roleAtLeast = (role: MemberRole, floor: MemberRole): boolean => MEMBER_ROLE_RANK[role] >= MEMBER_ROLE_RANK[floor];
|
|
24
24
|
// Rotating a door credential (event webhook, release gate, bug intake): the old value stops working immediately; the
|
|
25
25
|
// new one is shown once to be saved.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// system-prompt: what a conversation was told before its own words
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { SystemPromptModeSchema } from "./settings.js";
|
|
4
|
+
|
|
5
|
+
// Every section names its mechanism: the reader asks "did my AGENTS.md arrive", not "what does paragraph nine say".
|
|
6
|
+
export const PromptSectionSourceSchema = z.enum(["guidance", "persona", "field-notes", "memory"]);
|
|
7
|
+
export type PromptSectionSource = z.infer<typeof PromptSectionSourceSchema>;
|
|
8
|
+
|
|
9
|
+
export const PromptSectionSchema = z.object({
|
|
10
|
+
source: PromptSectionSourceSchema.describe("Which mechanism added this."),
|
|
11
|
+
title: z.string().describe("The one line a reader sees on the row that opens to the text below."),
|
|
12
|
+
text: z.string().describe("The section's exact words, as the model received them."),
|
|
13
|
+
});
|
|
14
|
+
export type PromptSection = z.infer<typeof PromptSectionSchema>;
|
|
15
|
+
|
|
16
|
+
// `runtime`: a CLI that keeps its own prompt, so no text; `trimmed`: the small-window paragraph, never reported as `custom`.
|
|
17
|
+
export const PromptBaseKindSchema = z.enum(["intentic", "claude", "custom", "runtime", "trimmed"]);
|
|
18
|
+
export type PromptBaseKind = z.infer<typeof PromptBaseKindSchema>;
|
|
19
|
+
|
|
20
|
+
export const PromptBaseSchema = z.object({
|
|
21
|
+
kind: PromptBaseKindSchema.describe("Which prompt the additions ride on."),
|
|
22
|
+
text: z.string().optional().describe("The base's own words, when they can be read here. Absent for a runtime that keeps its prompt to itself."),
|
|
23
|
+
model: z.string().optional().describe("The model the turn ran on, for a built-in base: Claude Code renders a different preset for each."),
|
|
24
|
+
});
|
|
25
|
+
export type PromptBase = z.infer<typeof PromptBaseSchema>;
|
|
26
|
+
|
|
27
|
+
// Recorded at dispatch from the request the adapter was handed: what was sent, not what would be composed now.
|
|
28
|
+
export const SystemPromptDisclosureSchema = z.object({
|
|
29
|
+
at: z.number().describe("When the turn that was told this was sent (epoch ms)."),
|
|
30
|
+
runtime: z.string().describe("Which runtime served that turn."),
|
|
31
|
+
mode: SystemPromptModeSchema.describe("Which base the turn was configured to run on."),
|
|
32
|
+
base: PromptBaseSchema,
|
|
33
|
+
sections: z.array(PromptSectionSchema).describe("What the daemon added to that base, in the order the model reads them."),
|
|
34
|
+
});
|
|
35
|
+
export type SystemPromptDisclosure = z.infer<typeof SystemPromptDisclosureSchema>;
|
|
36
|
+
|
|
37
|
+
// Absent `prompt` means no turn was dispatched since the daemon started recording.
|
|
38
|
+
export const ConversationPromptSchema = z.object({
|
|
39
|
+
prompt: SystemPromptDisclosureSchema.optional().describe("What the most recent turn of this conversation was told, if one has been recorded."),
|
|
40
|
+
});
|
|
41
|
+
export type ConversationPrompt = z.infer<typeof ConversationPromptSchema>;
|
package/src/schemas/webext.ts
CHANGED
|
@@ -34,7 +34,7 @@ export const WebExtGrantSchema = object({
|
|
|
34
34
|
mode: zEnum(["read", "act"]),
|
|
35
35
|
});
|
|
36
36
|
export type WebExtGrant = z.infer<typeof WebExtGrantSchema>;
|
|
37
|
-
// What a connected browser reports about itself: mirrors
|
|
37
|
+
// What a connected browser reports about itself: mirrors DeviceFacts, adding exactly which sites it may touch right now.
|
|
38
38
|
export const WebExtFactsSchema = object({
|
|
39
39
|
// How a person would name it: "Chrome 141 on Windows".
|
|
40
40
|
browser: string(),
|
package/src/schemas/workflows.ts
CHANGED
|
@@ -107,15 +107,15 @@ export const WorkflowGateSchema = z.object({
|
|
|
107
107
|
),
|
|
108
108
|
});
|
|
109
109
|
export type WorkflowGate = z.infer<typeof WorkflowGateSchema>;
|
|
110
|
-
// Small next to the
|
|
110
|
+
// Small next to the Visitor chat's 200: a gate run is a whole graph of sessions, not one turn.
|
|
111
111
|
export const GATE_DAILY_MAX_DEFAULT = 20;
|
|
112
112
|
// blocked means the gate couldn't reach a judgment, not that the product is broken; conflating the two makes teams turn
|
|
113
113
|
// the gate off. Maps to a neutral pipeline exit, never a failed build.
|
|
114
|
-
export const
|
|
115
|
-
export type
|
|
114
|
+
export const GuardOutcomeSchema = z.enum(["pass", "fail", "blocked"]);
|
|
115
|
+
export type GuardOutcome = z.infer<typeof GuardOutcomeSchema>;
|
|
116
116
|
// value is the field as the step wrote it; absent means most blocked verdicts had nothing to read.
|
|
117
117
|
export const GateVerdictSchema = z.object({
|
|
118
|
-
outcome:
|
|
118
|
+
outcome: GuardOutcomeSchema.describe(
|
|
119
119
|
"Ship it, do not, or we could not tell. That third answer exists because could not reach a judgement is not the product is broken: a gate that reported its own outages as failures is one a team switches off, so it should be the honest answer far more often than the convenient one, and it means a neutral build rather than a red one.",
|
|
120
120
|
),
|
|
121
121
|
// Why, in one line; realistically the only part of this a pipeline log will show.
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { packageRoot } from "@intentic/constants/node";
|
|
4
|
-
import {
|
|
4
|
+
import { test, expect } from "bun:test";
|
|
5
5
|
import { currentLock } from "./contract-lock.js";
|
|
6
6
|
|
|
7
7
|
// contract.lock.json must match the exported schemas; the git-aware half of this check lives in contract-shrink.mjs.
|
|
8
|
-
// Needs 30s: serializing ~500 schemas can run past
|
|
9
|
-
test(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
// Needs 30s: serializing ~500 schemas can run past the unit hang detector under load.
|
|
9
|
+
test(
|
|
10
|
+
"contract.lock.json matches the schemas this package exports",
|
|
11
|
+
() => {
|
|
12
|
+
const committed = JSON.parse(readFileSync(join(packageRoot(import.meta.url), "contract.lock.json"), "utf8")) as Record<string, unknown>;
|
|
13
|
+
expect(
|
|
14
|
+
currentLock(),
|
|
15
|
+
"the wire contract moved — run `pnpm --filter @intentic/sandbox-contract lock` and commit contract.lock.json with this change. " +
|
|
16
|
+
"If a schema or field was removed or changed (not added), land it as a `type!:` commit with a `Breaking-Note:` trailer " +
|
|
17
|
+
"saying, in the user's words, what stops working and what to do instead.",
|
|
18
|
+
).toEqual(committed);
|
|
19
|
+
},
|
|
20
|
+
{ timeout: 30_000 },
|
|
21
|
+
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { test, expect } from "bun:test";
|
|
2
2
|
import { ciFixConversationId, fixAttemptId } from "../ids/conversation-ids.js";
|
|
3
3
|
import type { AgentSummary } from "../schemas/agents.js";
|
|
4
4
|
import { planFixAttempt } from "./fix-attempt-plan.js";
|
|
@@ -36,6 +36,15 @@ test("an attempt that ended is continued by a plain press, and by an explicit Co
|
|
|
36
36
|
expect(planFixAttempt(BASE, roster, [], `continue`)).toEqual({ kind: `continue`, conversationId: BASE, attempt: 1 });
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
// A nudge to carry on names evidence earlier in the conversation, which an attempt turned away never received.
|
|
40
|
+
test("an attempt turned away at the door is sent the whole task again, in the same conversation", () => {
|
|
41
|
+
const roster = [agent(BASE, { status: `error`, failureCode: `sandbox-memory-low`, failure: `Sandbox memory is low.` })];
|
|
42
|
+
expect(planFixAttempt(BASE, roster, [])).toEqual({ kind: `resend`, conversationId: BASE, attempt: 1 });
|
|
43
|
+
expect(planFixAttempt(BASE, roster, [], `continue`)).toEqual({ kind: `resend`, conversationId: BASE, attempt: 1 });
|
|
44
|
+
// Starting over is still the reader's to ask for.
|
|
45
|
+
expect(planFixAttempt(BASE, roster, [], `start-over`)).toMatchObject({ kind: `start-over`, retire: BASE });
|
|
46
|
+
});
|
|
47
|
+
|
|
39
48
|
test("start over retires the latest attempt and numbers the next past it", () => {
|
|
40
49
|
const second = fixAttemptId(BASE, 2);
|
|
41
50
|
const plan = planFixAttempt(BASE, [agent(BASE, { status: `stopped` }), agent(second, { status: `error` })], [], `start-over`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fixAttemptOf, latestFixAttempt, nextFixAttemptId } from "../ids/conversation-ids.js";
|
|
2
2
|
import type { AgentSummary } from "../schemas/agents.js";
|
|
3
|
-
import { type FixStance, fixStance } from "./fix-stance.js";
|
|
3
|
+
import { type FixStance, fixStance, turnedAwayAttempt } from "./fix-stance.js";
|
|
4
4
|
|
|
5
5
|
/* WHAT A PRESS ON A FAILURE'S FIX CONTROL DOES, decided once for every surface that has one — the push question in the shell, the pipelines board's rows. */
|
|
6
6
|
|
|
@@ -12,6 +12,8 @@ export type FixAttemptPlan =
|
|
|
12
12
|
| { readonly kind: "new"; readonly conversationId: string; readonly attempt: number }
|
|
13
13
|
// The latest attempt ended without a fix: carry on in it, with a nudge rather than the whole opening prompt.
|
|
14
14
|
| { readonly kind: "continue"; readonly conversationId: string; readonly attempt: number }
|
|
15
|
+
// The latest attempt was turned away before it started, so it never saw the task: the whole prompt again, in it.
|
|
16
|
+
| { readonly kind: "resend"; readonly conversationId: string; readonly attempt: number }
|
|
15
17
|
// Set the latest attempt aside (stopped first if still running) and open the next one on a clean worktree.
|
|
16
18
|
| {
|
|
17
19
|
readonly kind: "start-over";
|
|
@@ -40,7 +42,9 @@ export const planFixAttempt = (base: string, roster: readonly AgentSummary[], kn
|
|
|
40
42
|
return { kind: "start-over", retire: latest.agent.id, stopFirst: stance.kind === "working", ...next() };
|
|
41
43
|
}
|
|
42
44
|
if (stance.retry) {
|
|
43
|
-
|
|
45
|
+
// A nudge to carry on names evidence earlier in the conversation, which an attempt turned away never received.
|
|
46
|
+
const kind = turnedAwayAttempt(latest.agent) ? "resend" : "continue";
|
|
47
|
+
return { kind, conversationId: latest.agent.id, attempt: latest.attempt };
|
|
44
48
|
}
|
|
45
49
|
return { kind: "busy", conversationId: latest.agent.id, stance };
|
|
46
50
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { test, expect } from "bun:test";
|
|
2
2
|
import { fixStance } from "./fix-stance.js";
|
|
3
3
|
import type { AgentSummary } from "../schemas/agents.js";
|
|
4
4
|
|
|
@@ -97,3 +97,12 @@ test("an unknown ending still produces a stance", () => {
|
|
|
97
97
|
expect(stance.label).toBe(`Agent stopped`);
|
|
98
98
|
expect(stance.retry).toBe(true);
|
|
99
99
|
});
|
|
100
|
+
|
|
101
|
+
// Refused before the model saw a word, so there is no work to carry on: the chip says it never started, and the press
|
|
102
|
+
// sends the whole task rather than a nudge that points at evidence the attempt never received.
|
|
103
|
+
test("an attempt turned away at the door says it never started, with the refusal's own sentence", () => {
|
|
104
|
+
const stance = fixStance(agent({ status: `error`, failureCode: `sandbox-memory-low`, failure: `Sandbox memory is low` }));
|
|
105
|
+
expect(stance).toMatchObject({ kind: `ended`, ongoing: false, retry: true, label: `Didn't start` });
|
|
106
|
+
expect(stance.hint).toContain(`turned away before it started: Sandbox memory is low.`);
|
|
107
|
+
expect(stance.hint).toContain(`sends it the whole task again`);
|
|
108
|
+
});
|
package/src/state/fix-stance.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { turnedAwayCode } from "../policy/turned-away.js";
|
|
1
2
|
import type { AgentSummary } from "../schemas/agents.js";
|
|
2
3
|
|
|
3
4
|
/* WHAT BECAME OF THE AGENT A SURFACE SENT AFTER A FAILURE — a red pipeline row, a refused push. */
|
|
@@ -87,7 +88,19 @@ const ENDINGS: Partial<Record<AgentSummary["status"], { readonly label: string;
|
|
|
87
88
|
idle: { label: "Nothing changed", why: "The fix agent finished without changing any files" },
|
|
88
89
|
};
|
|
89
90
|
|
|
91
|
+
// A failed turn the refusal stopped before the model saw a word: it never started, so there is nothing to carry on.
|
|
92
|
+
export const turnedAwayAttempt = (agent: AgentSummary): boolean => agent.status === "error" && turnedAwayCode(agent.failureCode);
|
|
93
|
+
|
|
90
94
|
const ended = (agent: AgentSummary): FixStance => {
|
|
95
|
+
if (turnedAwayAttempt(agent)) {
|
|
96
|
+
return {
|
|
97
|
+
kind: "ended",
|
|
98
|
+
ongoing: false,
|
|
99
|
+
retry: true,
|
|
100
|
+
label: "Didn't start",
|
|
101
|
+
hint: `The fix agent was turned away before it started${agent.failure === undefined ? "" : `: ${agent.failure}`}. Continuing sends it the whole task again; starting over opens a fresh conversation.`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
91
104
|
const ending = ENDINGS[agent.status] ?? { label: "Agent stopped", why: "The fix agent's turn ended" };
|
|
92
105
|
// File count left behind by a turn that died mid-edit; not a fix, but worth surfacing.
|
|
93
106
|
const files = agent.diff?.files ?? 0;
|
|
@@ -19,12 +19,16 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
|
|
|
19
19
|
// Armed condition watches; carries safely: the journal holds no credential, only names re-derived on target.
|
|
20
20
|
{ path: "watches/", portability: "carry" },
|
|
21
21
|
{ path: "transcripts/", portability: "carry" },
|
|
22
|
+
// What each conversation's last turn was told before its own words (prompt-record.ts), one file per conversation.
|
|
23
|
+
// Carried with the transcripts it explains: a restored conversation whose prompt record stayed behind can no
|
|
24
|
+
// longer say whether its AGENTS.md arrived or a small window trimmed it, which is the whole point of keeping it.
|
|
25
|
+
{ path: "system-prompts/", portability: "carry" },
|
|
22
26
|
// One runtime session store per fenced conversation, holding for it what `.intentic/records/sessions/claude/` holds
|
|
23
27
|
// for every other: its own transcripts, plans, backups and checklists, kept off /work so no other conversation's
|
|
24
28
|
// namespace can reach them. Carried for the same reason that one is.
|
|
25
29
|
{ path: "sessions/", portability: "carry" },
|
|
26
30
|
// What each message can restore to (a checkpoint or isolated commit); the join between transcripts and scopes.
|
|
27
|
-
{ path: "turn-
|
|
31
|
+
{ path: "turn-checkpoints.json", portability: "carry" },
|
|
28
32
|
// Index of published-page conversations; the pages travel with /work anyway, this makes one withdrawable.
|
|
29
33
|
{ path: "shares.json", portability: "carry" },
|
|
30
34
|
{ path: "activity.jsonl", portability: "carry" },
|
|
@@ -60,6 +64,18 @@ export const HISTORY_STATE_FILES: readonly StateFile[] = [
|
|
|
60
64
|
},
|
|
61
65
|
// Search phrase index, derived from transcripts/; rebuilt by the first boot's backfill instead of carried.
|
|
62
66
|
{ path: "said-index/", portability: "derived", note: "Rebuilt from the carried transcripts by the first boot's backfill." },
|
|
67
|
+
// What each conversation was last told (prompt-record.ts). Tens of kilobytes per conversation, rewritten by every
|
|
68
|
+
// turn — and a prompt composed on THIS sandbox names its paths, devices and accounts, so carrying it would describe
|
|
69
|
+
// the machine the bundle left rather than the one it landed on.
|
|
70
|
+
{
|
|
71
|
+
path: "system-prompts/",
|
|
72
|
+
portability: "derived",
|
|
73
|
+
note: "Each conversation records what it was told again on its next turn.",
|
|
74
|
+
},
|
|
75
|
+
// What each connected machine and browser publishes as its tools, so one that is asleep still appears in a turn
|
|
76
|
+
// (peers/peer-tool-memory.ts). Re-asked of every peer the moment it reconnects, and about software that stayed
|
|
77
|
+
// behind on somebody else's computer, so it is not worth carrying.
|
|
78
|
+
{ path: "peer-tools.json", portability: "derived" },
|
|
63
79
|
{ path: "overlays/", portability: "derived" },
|
|
64
80
|
{ path: "logs/", portability: "derived" },
|
|
65
81
|
{ path: "trash/", portability: "derived" },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import { RUNTIME_DOMAIN_BINDINGS,
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { RUNTIME_DOMAIN_BINDINGS, staleRuntimeQueryKeys } from "./runtime-state.js";
|
|
3
3
|
|
|
4
4
|
describe(`staleRuntimeQueryKeys`, () => {
|
|
5
5
|
it(`maps a pushed domain to the queries it makes stale`, () => {
|
|
@@ -30,13 +30,7 @@ describe(`staleRuntimeQueryKeys`, () => {
|
|
|
30
30
|
});
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
describe(`
|
|
34
|
-
it(`covers every declared domain, since a reconnect is the only recovery for a frame nobody received`, () => {
|
|
35
|
-
// Joined into strings for comparison: two equal arrays are still different object identities.
|
|
36
|
-
const joined = (keys: readonly (readonly string[])[]): string[] => [...new Set(keys.map((key) => key.join(`/`)))].toSorted();
|
|
37
|
-
expect(joined(runtimeBoundQueryKeys())).toEqual(joined(RUNTIME_DOMAIN_BINDINGS.flatMap((binding) => binding.invalidates)));
|
|
38
|
-
});
|
|
39
|
-
|
|
33
|
+
describe(`RUNTIME_DOMAIN_BINDINGS`, () => {
|
|
40
34
|
it(`leaves no domain declaring no keys: a domain nothing renders has no reason to be pushed`, () => {
|
|
41
35
|
expect(RUNTIME_DOMAIN_BINDINGS.filter((binding) => binding.invalidates.length === 0)).toEqual([]);
|
|
42
36
|
});
|
|
@@ -63,7 +63,3 @@ const dedupe = (keys: readonly QueryKeyPath[]): readonly QueryKeyPath[] => [...n
|
|
|
63
63
|
// browser share one table. An unknown domain is dropped rather than throwing, for forward compatibility.
|
|
64
64
|
export const staleRuntimeQueryKeys = (domains: readonly string[]): readonly QueryKeyPath[] =>
|
|
65
65
|
dedupe(RUNTIME_DOMAIN_BINDINGS.filter((binding) => domains.includes(binding.domain)).flatMap((binding) => binding.invalidates));
|
|
66
|
-
|
|
67
|
-
// Every key any runtime domain feeds; what a fresh /events connection re-asks wholesale, since a missed push here is
|
|
68
|
-
// gone for good. Bounds the cost to one read per key, letting these views stay unpolled.
|
|
69
|
-
export const runtimeBoundQueryKeys = (): readonly QueryKeyPath[] => dedupe(RUNTIME_DOMAIN_BINDINGS.flatMap((binding) => binding.invalidates));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { STATE_DIR } from "@intentic/constants";
|
|
2
2
|
import type { FileContribution } from "@intentic/extension-manifest";
|
|
3
|
-
import { describe,
|
|
3
|
+
import { describe, it, expect } from "bun:test";
|
|
4
4
|
import {
|
|
5
5
|
BACKED_UP_STATE_PATHS,
|
|
6
6
|
isLockedWorkspacePath,
|
|
@@ -302,12 +302,12 @@ describe(`VERSIONED_STATE_PATHS`, () => {
|
|
|
302
302
|
// versioned asks if a human should review it; carry asks if it moves. Frequently-rewritten ledgers are carry only.
|
|
303
303
|
it(`leaves the ledgers and the bulk out even though they travel`, () => {
|
|
304
304
|
for (const path of [
|
|
305
|
-
|
|
305
|
+
`${STATE_DIR}/records/workflow-runs.json`,
|
|
306
306
|
// Split from the tracked automations manifest so a run doesn't dirty it.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
307
|
+
`${STATE_DIR}/records/automation-runs.json`,
|
|
308
|
+
`${STATE_DIR}/records/loops.json`,
|
|
309
|
+
`${STATE_DIR}/records/thread-sessions.json`,
|
|
310
|
+
`${STATE_DIR}/records/extension-usage.json`,
|
|
311
311
|
`.intentic/records/webchat-installs.json`,
|
|
312
312
|
`.intentic/records/sessions/claude/`,
|
|
313
313
|
`.intentic/records/artifacts/`,
|
|
@@ -529,7 +529,12 @@ describe(`BACKED_UP_STATE_PATHS`, () => {
|
|
|
529
529
|
|
|
530
530
|
// identity means never restoring into another sandbox; that doesn't stop the owner keeping their own copy.
|
|
531
531
|
it(`copies the ownership records that may never travel`, () => {
|
|
532
|
-
for (const path of [
|
|
532
|
+
for (const path of [
|
|
533
|
+
`.intentic/identity/owner.json`,
|
|
534
|
+
`.intentic/identity/members.json`,
|
|
535
|
+
`.intentic/identity/workspace.json`,
|
|
536
|
+
`.intentic/identity/passkeys.json`,
|
|
537
|
+
]) {
|
|
533
538
|
expect([path, BACKED_UP_STATE_PATHS.includes(path)]).toEqual([path, true]);
|
|
534
539
|
}
|
|
535
540
|
});
|
|
@@ -225,25 +225,25 @@ const STATE_FILES = [
|
|
|
225
225
|
{
|
|
226
226
|
path: ".intentic/records/webchat-installs.json",
|
|
227
227
|
invalidates: [],
|
|
228
|
-
why: "Which origins have loaded a
|
|
228
|
+
why: "Which origins have loaded a Visitor chat's widget, written on a 30s flush timer while a customer's site serves page views. The install panel that renders it fetches on open and polls itself while it is on screen, which is the whole window in which the answer changes for anyone. Pushing instead would bill every connected browser a refetch per flush, for a panel almost nobody has open.",
|
|
229
229
|
portability: "carry",
|
|
230
230
|
},
|
|
231
231
|
{
|
|
232
232
|
path: ".intentic/records/issue-installs.json",
|
|
233
233
|
invalidates: [],
|
|
234
|
-
why: "The same probe for the bug reporter's script, on the same flush timer and read by the same kind of panel, so it is outside the push path for the same reason the
|
|
234
|
+
why: "The same probe for the bug reporter's script, on the same flush timer and read by the same kind of panel, so it is outside the push path for the same reason the Visitor chat's is.",
|
|
235
235
|
portability: "carry",
|
|
236
236
|
},
|
|
237
237
|
{
|
|
238
238
|
path: ".intentic/records/webchat-outbox.json",
|
|
239
239
|
invalidates: [],
|
|
240
|
-
why: "
|
|
240
|
+
why: "Visitor chat replies a visitor has not collected yet, written when an approved wake answers or a human writes as the agent. The only reader is a stranger's browser polling the public /webchat door, which no query key in this app addresses; the owner's own view of the same words is the conversation's transcript, which the agent registry already pushes.",
|
|
241
241
|
portability: "carry",
|
|
242
242
|
},
|
|
243
243
|
{
|
|
244
244
|
path: ".intentic/records/thread-sessions.json",
|
|
245
245
|
invalidates: [],
|
|
246
|
-
why: "Thread bookkeeping (an inbound thread, a
|
|
246
|
+
why: "Thread bookkeeping (an inbound thread, a Visitor chat visitor, a Discord or Slack channel, → sandbox conversation + provider session), written on EVERY inbound message. Nothing in the browser reads it: what a thread produces is a conversation, and the fleet board already learns about that from the agent registry's own push. Naming a key here would bill every connected browser a refetch per inbound message, the request storm this table's own note warns about, to refresh nothing it can see.",
|
|
247
247
|
portability: "carry",
|
|
248
248
|
},
|
|
249
249
|
{
|
package/src/text/documents.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RequestDocument, ToolCallContent } from "../events/requests.js";
|
|
2
2
|
import { planParts } from "./title.js";
|
|
3
3
|
import { PLAN_DOCUMENTS_DIR } from "../state/workspace-state.js";
|
|
4
4
|
|
|
@@ -24,7 +24,7 @@ export const documentTitle = (markdown: string, path: string): string => planPar
|
|
|
24
24
|
|
|
25
25
|
// Reads the call's structured diff, not its result text: a Write's whole file already rides the frame as newText
|
|
26
26
|
// (capped, truncated flags it). Works even for a published transcript with no workspace behind it.
|
|
27
|
-
export const documentOf = (name: string, content: readonly ToolCallContent[] | undefined):
|
|
27
|
+
export const documentOf = (name: string, content: readonly ToolCallContent[] | undefined): RequestDocument | undefined => {
|
|
28
28
|
if (name.toLowerCase() !== "write") {
|
|
29
29
|
return undefined;
|
|
30
30
|
}
|
package/src/text/embed.test.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { webcrypto } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { test, expect, beforeAll, mock } from "bun:test";
|
|
3
|
+
import { stubGlobal, unstubAllGlobals } from "@intentic/testing/bun";
|
|
3
4
|
import { EmbedError, embedFailure, embedUrl, fetchEmbedJson, solveProofOfWork } from "./embed.js";
|
|
4
5
|
|
|
5
6
|
/* The wire every embed speaks before it speaks its own, against a fake fetch and the real WebCrypto. */
|
|
@@ -24,7 +25,9 @@ const leadingZeroBits = async (answer: string): Promise<number> => {
|
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
test("a door's route is spelled the daemon's way, with the automation id encoded", () => {
|
|
27
|
-
expect(embedUrl({ base: "https://sandbox.example", automationId: "a b" }, "webchat", "config")).toBe(
|
|
28
|
+
expect(embedUrl({ base: "https://sandbox.example", automationId: "a b" }, "webchat", "config")).toBe(
|
|
29
|
+
"https://sandbox.example/webchat/a%20b/config",
|
|
30
|
+
);
|
|
28
31
|
});
|
|
29
32
|
|
|
30
33
|
test("a refusal carries the server's own sentence and status, and a bodyless one says what it can", async () => {
|
|
@@ -35,11 +38,17 @@ test("a refusal carries the server's own sentence and status, and a bodyless one
|
|
|
35
38
|
});
|
|
36
39
|
|
|
37
40
|
test("a JSON fetch answers the body or throws the refusal", async () => {
|
|
38
|
-
|
|
41
|
+
stubGlobal(
|
|
42
|
+
"fetch",
|
|
43
|
+
mock(async () => new Response(JSON.stringify({ ok: true }), { status: 200 })),
|
|
44
|
+
);
|
|
39
45
|
expect(await fetchEmbedJson<{ ok: boolean }>("https://x/y")).toEqual({ ok: true });
|
|
40
|
-
|
|
46
|
+
stubGlobal(
|
|
47
|
+
"fetch",
|
|
48
|
+
mock(async () => new Response(JSON.stringify({ error: "rate limited" }), { status: 429 })),
|
|
49
|
+
);
|
|
41
50
|
await expect(fetchEmbedJson("https://x/y")).rejects.toMatchObject({ message: "rate limited", status: 429 });
|
|
42
|
-
|
|
51
|
+
unstubAllGlobals();
|
|
43
52
|
});
|
|
44
53
|
|
|
45
54
|
/* The one test that pays for real work: 12 bits is ~900 awaited digests. */
|
|
@@ -59,6 +68,8 @@ test("a different salt yields a different answer: a solution cannot be replayed
|
|
|
59
68
|
|
|
60
69
|
test("an http:// page is told the truth, in the embed's own words, instead of hanging on a missing SubtleCrypto", async () => {
|
|
61
70
|
Object.defineProperty(globalThis, "crypto", { value: { randomUUID: webcrypto.randomUUID }, configurable: true });
|
|
62
|
-
await expect(solveProofOfWork({ salt: "abc", difficulty: 8 }, "This page must be served over HTTPS to start a chat.")).rejects.toThrow(
|
|
71
|
+
await expect(solveProofOfWork({ salt: "abc", difficulty: 8 }, "This page must be served over HTTPS to start a chat.")).rejects.toThrow(
|
|
72
|
+
/HTTPS to start a chat/,
|
|
73
|
+
);
|
|
63
74
|
Object.defineProperty(globalThis, "crypto", { value: webcrypto, configurable: true });
|
|
64
75
|
});
|
package/src/text/emoji.test.ts
CHANGED