@intentic/sandbox-contract 1.217.0 → 1.218.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/src/schemas.ts CHANGED
@@ -185,7 +185,7 @@ export const AgentTurnSchema = z
185
185
  // first turn keeps its user-chosen title); an existing entry's title always wins.
186
186
  title: z.string().max(80).optional(),
187
187
  // Workspace-relative paths of files the user attached, already uploaded via /workspace/upload
188
- // (the browser puts them under .intentic/artifacts/attachments/<uuid>/<name>). The daemon hands them to the
188
+ // (the browser puts them under .intentic/records/artifacts/attachments/<uuid>/<name>). The daemon hands them to the
189
189
  // provider: Claude reads them from disk via its Read tool; Codex gets images as native inputs.
190
190
  attachments: z.array(z.string().min(1)).max(20).optional(),
191
191
  // Which provider (model + account) serves the turn; absent = claude. A sessionId only resumes on the
@@ -278,7 +278,7 @@ export const AgentTurnSchema = z
278
278
  * It is the birth half of the turn's taint (guard/turn-taint.ts). The other half marks itself as the
279
279
  * turn works — a fetched page, a foreign MCP server's answer — and together they are what the command
280
280
  * gate reads before letting a command read credential material unasked. Distinct from `unattended`,
281
- * which is about whether anyone is WATCHING: a Doorbell wake is both, an owner asking the agent to read
281
+ * which is about whether anyone is WATCHING: a Front Desk wake is both, an owner asking the agent to read
282
282
  * a web page is neither, and each flag governs a different decision. */
283
283
  outsideWake: z.string().min(1).optional(),
284
284
  // How tool calls are gated for this turn (the SDK's permissionMode, verbatim). 'plan' runs the
@@ -289,7 +289,7 @@ export const AgentTurnSchema = z
289
289
  /* Narrows the turn to these tool names (the SDK option of the same name — not to be confused with the
290
290
  * daemon's MCP `tools`, which are servers). Absent ⇒ every tool the runtime has, which is what an
291
291
  * owner-driven chat wants. Set by the automation dispatchers from Automation.allowedTools: a wake driven
292
- * by an OUTSIDE message runs bypassPermissions like any other automation turn, so for a public Doorbell
292
+ * by an OUTSIDE message runs bypassPermissions like any other automation turn, so for a public Front Desk
293
293
  * this list is the actual boundary — prompt wording is advice, an empty toolbox is not. */
294
294
  allowedTools: z.array(z.string().min(1)).optional(),
295
295
  effort: z.string().optional(),
@@ -505,13 +505,13 @@ export type Loop = z.infer<typeof LoopSchema>;
505
505
  // saved loop that cannot converge is a trap everyone who picks it afterwards pays a full run to discover.
506
506
  export const loopCanConverge = (loop: Pick<Loop, "output" | "checks">): boolean => loop.output.kind !== "none" || loop.checks.length > 0;
507
507
 
508
- /* Where a loop keeps what it must not lose between iterations: <workspace>/.intentic/artifacts/loops/<conversationId>/.
508
+ /* Where a loop keeps what it must not lose between iterations: <workspace>/.intentic/records/artifacts/loops/<conversationId>/.
509
509
  *
510
510
  * Under `.intentic` for the reason the acceptance runs are — it is outside every repo and bound back SHARED
511
511
  * into an isolated turn's worktree, so the agent writes and the browser reads the same tree, with nothing to
512
512
  * land and no git noise. `progress.md` is the loop's memory in `fresh` mode and its audit trail in `continue`
513
513
  * mode; `iteration-<n>.json` is the verdict a `claim` stop reads. */
514
- export const LOOP_DIR = `${STATE_DIR}/artifacts/loops`;
514
+ export const LOOP_DIR = `${STATE_DIR}/records/artifacts/loops`;
515
515
 
516
516
  // Why an iteration ended, which is not the same question as how the LOOP ended. `continue` is the ordinary
517
517
  // "not done yet"; `error` is a turn that surfaced an error frame, which does NOT end the loop by itself — a
@@ -1501,7 +1501,7 @@ export const SessionSummarySchema = z.object({
1501
1501
  export type SessionSummary = z.infer<typeof SessionSummarySchema>;
1502
1502
  export const SessionsListSchema = z.object({ sessions: z.array(SessionSummarySchema) });
1503
1503
 
1504
- // ---- settings: per-sandbox agent settings (.intentic/settings.json) ----
1504
+ // ---- settings: per-sandbox agent settings (.intentic/config/settings.json) ----
1505
1505
 
1506
1506
  // Which prompt the agent is, before this turn composes anything on top. Two built-in bases and an escape
1507
1507
  // hatch: Intentic's own (the default), Claude Code's preset, or the owner's text. Declared out here rather
@@ -1625,7 +1625,7 @@ export type RuleFirings = z.infer<typeof RuleFiringsSchema>;
1625
1625
  * adding it.
1626
1626
  *
1627
1627
  * builtin this image ships it — a baked tool's cheatsheet, or a core feature's
1628
- * own the owner wrote it (.intentic/skills/), and only these are editable here
1628
+ * own the owner wrote it (.intentic/config/skills/), and only these are editable here
1629
1629
  * capability something connected brought it: a CLI tool, a machine, a browser account, a VPN
1630
1630
  * extension an installed extension ships it inside its checkout
1631
1631
  * plugin a plugin capability cloned a repo that holds it
@@ -3168,7 +3168,7 @@ export const EnrollHostInputSchema = z.object({
3168
3168
  });
3169
3169
  export type EnrollHostInput = z.infer<typeof EnrollHostInputSchema>;
3170
3170
 
3171
- // ---- capabilities: the sandbox's unified capability manifest (.intentic/capabilities.json) ----
3171
+ // ---- capabilities: the sandbox's unified capability manifest (.intentic/config/capabilities.json) ----
3172
3172
  // Everything a user adds to a sandbox is a capability with an idempotent apply + a status check. The manifest is
3173
3173
  // the source of truth for what's active; `mcp`-kind entries also feed the agent's MCP servers each turn. DevOps
3174
3174
  // is the capability that scaffolds the intent/desired-state repos — until it's active the sandbox is empty.
@@ -3422,7 +3422,7 @@ export const DockerConfigSchema = z.object({
3422
3422
  });
3423
3423
  // A logged-in browser session the AGENT drives via Playwright MCP tools — for social platforms whose APIs can't
3424
3424
  // cover "all the actions" (X reads are paywalled; X community-join and YouTube community-posts have no API). The
3425
- // session lives in a persisted Chromium profile under .intentic/browser/<id>, established through the guided-login
3425
+ // session lives in a persisted Chromium profile under .intentic/local/browser/<id>, established through the guided-login
3426
3426
  // WebSocket (/system/browser-login) or by the agent signing in itself. Chromium itself rides this kind's
3427
3427
  // Dockerfile fragment, applied on an owner rebuild.
3428
3428
  //
@@ -3793,12 +3793,12 @@ export type Persona = z.infer<typeof PersonaSchema>;
3793
3793
  /* THE ONE CARD ID THE PRODUCT NAMES ITSELF — the read-only persona a public web chat answers through.
3794
3794
  *
3795
3795
  * Nothing else is stock: a fresh workspace has no personas at all, and every card on the Personas page is one
3796
- * the owner wrote. This id is the exception because a Doorbell is driven by a stranger with nobody watching, so
3796
+ * the owner wrote. This id is the exception because a Front Desk is driven by a stranger with nobody watching, so
3797
3797
  * it is the one wake whose bounds cannot be left to the prompt's wording — the daemon writes the card the moment
3798
- * a Doorbell is saved (personas/front-desk.ts) and the automations form fills a blank Doorbell persona with it.
3798
+ * a Front Desk is saved (personas/front-desk.ts) and the automations form fills a blank Front Desk persona with it.
3799
3799
  *
3800
3800
  * It lives HERE because those two are in different packages and must agree exactly. A literal in each would
3801
- * drift into a Doorbell pinned to a card nobody creates, and turnPersona answers a missing card by denying
3801
+ * drift into a Front Desk pinned to a card nobody creates, and turnPersona answers a missing card by denying
3802
3802
  * everything — a public chat that cannot even read, which is safe and useless.
3803
3803
  *
3804
3804
  * It is FRONT DESK and not "visitor": the card is who answers the people who arrive, not the person arriving. */
@@ -4211,10 +4211,10 @@ export const ExtensionSummarySchema = z.object({
4211
4211
  commit: z.string(),
4212
4212
  /* Where the code comes from — which is also what the web varies per row: `builtin` (image-baked, no git
4213
4213
  * checkout, not removable) hides the uninstall affordance, `installed` (a git capability) shows the pinned
4214
- * commit, `workspace` (a directory under .intentic/workspace-extensions/, created and edited in place —
4214
+ * commit, `workspace` (a directory under .intentic/config/workspace-extensions/, created and edited in place —
4215
4215
  * typically by an agent) is "uninstalled" by deleting its directory. */
4216
4216
  source: z.enum(["builtin", "installed", "workspace"]),
4217
- // The owner's switch (.intentic/extension-enablement.json). A disabled extension is still listed — that's
4217
+ // The owner's switch (.intentic/config/extension-enablement.json). A disabled extension is still listed — that's
4218
4218
  // what makes it switchable back on — but the daemon wires none of its contributions up and the web host
4219
4219
  // doesn't activate it.
4220
4220
  enabled: z.boolean(),
@@ -4268,7 +4268,7 @@ export const ExtensionsListSchema = z.object({
4268
4268
  // what lets the tab say "checked an hour ago" instead of presenting staleness as certainty.
4269
4269
  updatesCheckedAt: z.string().optional(),
4270
4270
  });
4271
- // The extension's contributes.settings values, persisted daemon-side (.intentic/extension-settings.json) keyed
4271
+ // The extension's contributes.settings values, persisted daemon-side (.intentic/config/extension-settings.json) keyed
4272
4272
  // by the manifest-derived extension id — the checkout stays pristine, so a re-clone update never loses them.
4273
4273
  // Secret-marked values are stripped from `settings`; `secretsSet` lists the secret keys that DO hold a value,
4274
4274
  // so the UI renders "•••• (set)" without ever receiving the secret back.
@@ -4310,7 +4310,7 @@ export const ExtensionProcessStatusSchema = z.object({
4310
4310
  });
4311
4311
  export type ExtensionProcessStatus = z.infer<typeof ExtensionProcessStatusSchema>;
4312
4312
 
4313
- // ---- automations: scheduled agent wake-ups (.intentic/automations.json) ----
4313
+ // ---- automations: scheduled agent wake-ups (.intentic/config/automations.json) ----
4314
4314
  // An automation wakes the agent autonomously: the daemon's scheduler fires each enabled automation on its
4315
4315
  // trigger, runs the optional guard command (a shell command in the workspace; non-zero exit skips the wake),
4316
4316
  // then runs one agent turn with the prompt. The manifest is user config; run history is daemon-recorded.
@@ -4412,7 +4412,7 @@ export const TriggerSchema = z.discriminatedUnion("kind", [
4412
4412
  ]);
4413
4413
  export type Trigger = z.infer<typeof TriggerSchema>;
4414
4414
 
4415
- /* The Doorbell widget's settings — everything about the embeddable chat that isn't the automation's prompt.
4415
+ /* The Front Desk widget's settings — everything about the embeddable chat that isn't the automation's prompt.
4416
4416
  * Present only on `webchat` listener automations; the trigger keeps `allowedOrigins` because that one is the
4417
4417
  * admission gate the message route reads, not a rendering choice.
4418
4418
  *
@@ -4422,7 +4422,7 @@ export type Trigger = z.infer<typeof TriggerSchema>;
4422
4422
  * the second: a field added here is invisible to the widget until it is listed there. */
4423
4423
  export const WebchatConfigSchema = z.object({
4424
4424
  // `public` admits anyone; `google` refuses a message that carries no verifiable Google ID token. Absent ⇒
4425
- // public — a Doorbell with no access setting is the anonymous support box it looks like.
4425
+ // public — a Front Desk with no access setting is the anonymous support box it looks like.
4426
4426
  access: z.enum(["public", "google"]).optional(),
4427
4427
  // Ask an anonymous visitor for a display name before the first message. Cosmetic: the name is typed, so it
4428
4428
  // reaches the model as untrusted `displayName`, never as identity.
@@ -4456,7 +4456,7 @@ export const WebchatConfigSchema = z.object({
4456
4456
  *
4457
4457
  * `dailyMessageMax` absent ⇒ WEBCHAT_DAILY_MAX_DEFAULT, not uncapped. Every message here is an agent turn
4458
4458
  * billed to the owner, and the per-minute window bounds the RATE without bounding the DAY — twenty a minute,
4459
- * sustained, is tens of thousands of turns before anyone notices. A Doorbell nobody configured should not be
4459
+ * sustained, is tens of thousands of turns before anyone notices. A Front Desk nobody configured should not be
4460
4460
  * able to spend that, so the safe number is the one you get for free and the owner raises it deliberately.
4461
4461
  * `conversationMessageMax` stays optional-means-uncapped: it is per visitor thread, which the daily ceiling
4462
4462
  * already bounds in aggregate. */
@@ -4469,18 +4469,18 @@ export const WebchatConfigSchema = z.object({
4469
4469
  });
4470
4470
  export type WebchatConfig = z.infer<typeof WebchatConfigSchema>;
4471
4471
 
4472
- /* The daily agent-turn ceiling a Doorbell gets when its owner sets none. Lives here rather than beside the
4472
+ /* The daily agent-turn ceiling a Front Desk gets when its owner sets none. Lives here rather than beside the
4473
4473
  * route that enforces it because both ends need the number: the daemon to apply it, and the automation editor
4474
4474
  * to show the owner what they are already protected by (an invisible limit is one people hit and file as a bug).
4475
4475
  *
4476
- * 200 is chosen to be irrelevant to real support traffic and decisive against a script. A Doorbell answering
4476
+ * 200 is chosen to be irrelevant to real support traffic and decisive against a script. A Front Desk answering
4477
4477
  * two hundred questions in one UTC day is a busy one; a scripted flood reaches that in ten seconds and then
4478
4478
  * stops costing anything. */
4479
4479
  export const WEBCHAT_DAILY_MAX_DEFAULT = 200;
4480
4480
 
4481
4481
  /* ---- the widget wire: three shapes GET /webchat/<id>/config, GET …/challenge and POST …/message speak ----
4482
4482
  *
4483
- * They live here, beside the stored config they derive from, because the Doorbell widget is a SECOND client of
4483
+ * They live here, beside the stored config they derive from, because the Front Desk widget is a SECOND client of
4484
4484
  * this daemon — a bundle running on a stranger's page — and the reason this package exists is that both ends of
4485
4485
  * a wire read one definition. The widget imports these as types only (`import type`), so zod never reaches a
4486
4486
  * visitor's browser. */
@@ -4538,13 +4538,13 @@ export const AutomationSchema = z.object({
4538
4538
  // Shell command run in the workspace root before waking; exit 0 ⇒ wake, non-zero ⇒ the run is "skipped".
4539
4539
  guard: z.string().min(1).optional(),
4540
4540
  prompt: z.string().min(1),
4541
- // The Doorbell widget's settings — `webchat` listener automations only, ignored on every other trigger.
4541
+ // The Front Desk widget's settings — `webchat` listener automations only, ignored on every other trigger.
4542
4542
  webchat: WebchatConfigSchema.optional(),
4543
4543
  /* NARROW THIS ONE JOB FURTHER than the persona it runs as — raw tool names, and the escape hatch under the
4544
4544
  * shelves rather than the way anyone is expected to answer this question.
4545
4545
  *
4546
4546
  * The persona (`actsAs` below) is where a session's toolbox is decided now, because the answer is worth
4547
- * reusing: the same bounds apply to the chat, the workflow and the Doorbell that name the same card. This
4547
+ * reusing: the same bounds apply to the chat, the workflow and the Front Desk that name the same card. This
4548
4548
  * stays for the job that needs LESS than its persona allows — and only less, which is a rule the composer
4549
4549
  * enforces rather than a convention: an edit here can never hand back a shelf the persona switched off. */
4550
4550
  allowedTools: z.array(z.string().min(1)).optional(),
@@ -4557,14 +4557,14 @@ export const AutomationSchema = z.object({
4557
4557
  * A sandbox holds several accounts side by side, and when the first one is out of headroom — or belongs to
4558
4558
  * an organization that has disabled the plan — every fire of every automation errors against it until a
4559
4559
  * human happens to read the row. Pinning the wake to an account that can actually run is the difference
4560
- * between "my nightly sweep is quiet" and a Doorbell that turns visitors away all day. */
4560
+ * between "my nightly sweep is quiet" and a Front Desk that turns visitors away all day. */
4561
4561
  account: z.string().optional(),
4562
4562
  /* WHICH FACE THE WAKE SHOWS THE OUTSIDE WORLD (AgentTurnSchema.actsAs — read its note for why this is not
4563
4563
  * spelled `account`, which is the field directly above and means who PAYS).
4564
4564
  *
4565
4565
  * Absent ⇒ the wake reaches NO logged-in account at all. That is the one place this whole layer stops being
4566
4566
  * a convenience and becomes a boundary, and it is deliberately the strictest default in the schema: an
4567
- * automation fires with nobody at the composer, on a prompt that — for a Doorbell — a stranger helped write.
4567
+ * automation fires with nobody at the composer, on a prompt that — for a Front Desk — a stranger helped write.
4568
4568
  * `allowedTools` above already carries this exact reasoning for tools; an unrepeatable public post deserves
4569
4569
  * it at least as much. An automation that genuinely means "post as us" says so, once, in a field a reviewer
4570
4570
  * can see. */
@@ -4590,7 +4590,7 @@ export const AutomationSchema = z.object({
4590
4590
  });
4591
4591
  export type Automation = z.infer<typeof AutomationSchema>;
4592
4592
 
4593
- // A wake held for owner approval (.intentic/approvals/<id>.json, one file per held wake). It snapshots the
4593
+ // A wake held for owner approval (.intentic/records/approvals/<id>.json, one file per held wake). It snapshots the
4594
4594
  // trigger payload so an approved run replays exactly what fired, even across a daemon restart. The id is minted
4595
4595
  // by the daemon (an entryId-safe filename).
4596
4596
  export const AutomationApprovalSchema = z.object({
@@ -4606,7 +4606,7 @@ export const AutomationApprovalSchema = z.object({
4606
4606
  * already opened for it and the provider session that conversation last ran on.
4607
4607
  *
4608
4608
  * Snapshotted for the same reason the payload is, and it is the half that was missing: without it an
4609
- * approved wake fell through to minting a fresh conversation, so a Doorbell visitor's chat became one card
4609
+ * approved wake fell through to minting a fresh conversation, so a Front Desk visitor's chat became one card
4610
4610
  * per approved message instead of the single thread the dispatcher had opened for them — a second worktree
4611
4611
  * each time, and an agent that met the visitor again on every turn. Absent for a schedule or a webhook,
4612
4612
  * which own no thread. */
@@ -4718,7 +4718,7 @@ export type TriggerSource = z.infer<typeof TriggerSourceSchema>;
4718
4718
  * create — a shelf card on the page that makes the automation in one click, switched off, ready to read.
4719
4719
  * The chores are all of these: upkeep nobody opens an automations page hunting for.
4720
4720
  * configure — a shelf card that opens the dialog PREFILLED, for a template that cannot work unconfigured (a
4721
- * Doorbell with no allowed sites admits nobody, and a row that silently does nothing is worse
4721
+ * Front Desk with no allowed sites admits nobody, and a row that silently does nothing is worse
4722
4722
  * than a form). */
4723
4723
  export const TemplateOfferSchema = z.enum(["create", "configure"]);
4724
4724
 
@@ -4889,14 +4889,14 @@ export const WorkflowGateSchema = z.object({
4889
4889
  /* Runs per UTC day, across every caller. A gate is a paid endpoint reachable with no person in the loop:
4890
4890
  * one of these wired into a push-triggered pipeline is a fan-out of sessions per commit, and the
4891
4891
  * per-request deadline bounds one call's WALL CLOCK without bounding the day's SPEND. Absent ⇒
4892
- * GATE_DAILY_MAX_DEFAULT, not uncapped, for the reason the Doorbell's ceiling is not optional either.
4892
+ * GATE_DAILY_MAX_DEFAULT, not uncapped, for the reason the Front Desk's ceiling is not optional either.
4893
4893
  */
4894
4894
  dailyMax: z.number().int().positive().optional(),
4895
4895
  });
4896
4896
  export type WorkflowGate = z.infer<typeof WorkflowGateSchema>;
4897
4897
 
4898
- /* The gate's daily ceiling when its author sets none. Deliberately small next to the Doorbell's 200: a
4899
- * Doorbell message is one turn, a gate run is a whole graph of sessions, and the honest comparison is cost
4898
+ /* The gate's daily ceiling when its author sets none. Deliberately small next to the Front Desk's 200: a
4899
+ * Front Desk message is one turn, a gate run is a whole graph of sessions, and the honest comparison is cost
4900
4900
  * rather than count. Twenty is a busy day of merges and a script's first minute. */
4901
4901
  export const GATE_DAILY_MAX_DEFAULT = 20;
4902
4902
 
@@ -5235,7 +5235,7 @@ export const PrepushRunSchema = z.object({
5235
5235
  });
5236
5236
  export type PrepushRun = z.infer<typeof PrepushRunSchema>;
5237
5237
 
5238
- // ---- drafts: agent-proposed posts awaiting owner approval (.intentic/drafts/<id>.json) ----
5238
+ // ---- drafts: agent-proposed posts awaiting owner approval (.intentic/config/drafts/<id>.json) ----
5239
5239
  // One JSON file per draft. The AGENT creates drafts with its normal file tools — it can't call daemon routes,
5240
5240
  // the same split as the environment proposal — while the daemon edits/deletes them on the owner's behalf, so
5241
5241
  // the two writers never share a file. The id IS the filename (entryId charset ⇒ path-safe); the body never
@@ -5275,7 +5275,7 @@ export const DraftSchema = z.object({
5275
5275
  * address and one comment's permalink is the difference between talking to the room and answering the
5276
5276
  * person: the publisher opens exactly this and replies where it lands. */
5277
5277
  target: z.string().optional(),
5278
- // Workspace-relative attachment paths, e.g. ".intentic/drafts/media/chart.png".
5278
+ // Workspace-relative attachment paths, e.g. ".intentic/config/drafts/media/chart.png".
5279
5279
  media: z.array(z.string()).optional(),
5280
5280
  // Suggested post time (epoch ms, the at/nextRun convention). Optional — the agent may propose without a
5281
5281
  // date and the owner sets one at approval; an approved draft with no date posts as soon as it's picked up.
@@ -5305,7 +5305,7 @@ export type DraftSummary = z.infer<typeof DraftSummarySchema>;
5305
5305
  // draft would silently never post.
5306
5306
  export const DraftsListSchema = z.object({ drafts: z.array(DraftSummarySchema), invalid: z.array(z.string()) });
5307
5307
  export type DraftsList = z.infer<typeof DraftsListSchema>;
5308
- // entryId, not a bare string: the id becomes a filename under .intentic/drafts/.
5308
+ // entryId, not a bare string: the id becomes a filename under .intentic/config/drafts/.
5309
5309
  export const DraftIdParamSchema = z.object({ id: entryId });
5310
5310
 
5311
5311
  // ---- panels: per-repository dev servers + the content facts extensions detect on ----
@@ -5505,6 +5505,12 @@ export const MachinePairingSchema = z.object({
5505
5505
  // product surfaces these, so a file edited on both ends stays stuck until someone runs the CLI.
5506
5506
  conflicts: z.number().int().nonnegative().optional(),
5507
5507
  paused: z.boolean().optional(),
5508
+ /* The SECOND session's word — the one-way mirror carrying the sandbox's state dir down (sync's backupSpec).
5509
+ * Reported separately rather than folded into the status above, because the two fail independently and mean
5510
+ * different things: the first going quiet stops the owner's edits moving, the second going quiet stops their
5511
+ * personas, skills, automations, drafts and transcripts from surviving the sandbox. A backup that is not
5512
+ * running is only dangerous while nobody knows, so it gets its own word on the line. */
5513
+ backupStatus: z.string().optional(),
5508
5514
  });
5509
5515
  export type MachinePairing = z.infer<typeof MachinePairingSchema>;
5510
5516
 
@@ -5942,9 +5948,9 @@ export const SubagentIdParamSchema = z.object({ id: z.string() });
5942
5948
 
5943
5949
  // ---- environment: the overlay Dockerfile extending the sandbox image ----
5944
5950
  // The approved file is DAEMON-COMPOSED: pinned FROM + capability fragments + the owner-approved custom section.
5945
- // The agent drafts one file per thing it needs (.intentic/environment.d/<tool>.Dockerfile — custom-section
5951
+ // The agent drafts one file per thing it needs (.intentic/config/environment.d/<tool>.Dockerfile — custom-section
5946
5952
  // content only, no FROM) with its normal file tools, and the daemon folds those into the single proposal file
5947
- // (.intentic/environment.Dockerfile) the owner reads. The owner approves it in the browser, which stores it as
5953
+ // (.intentic/config/environment.Dockerfile) the owner reads. The owner approves it in the browser, which stores it as
5948
5954
  // the custom file and recomposes the approved artifact whose sha256 the rebuild executor pins. Both composed
5949
5955
  // files are written only when the composition CHANGES — see writeComposed, and the read loop it exists to end.
5950
5956
  // Status is derived, never stored:
@@ -5954,7 +5960,7 @@ export const SubagentIdParamSchema = z.object({ id: z.string() });
5954
5960
  const environmentFileSchema = z.object({ content: z.string(), hash: z.string() });
5955
5961
  export const EnvironmentSchema = z.object({
5956
5962
  proposal: environmentFileSchema.optional(),
5957
- // The owner-approved agent-written custom section (.intentic/environment.custom.Dockerfile).
5963
+ // The owner-approved agent-written custom section (.intentic/config/environment.custom.Dockerfile).
5958
5964
  custom: environmentFileSchema.optional(),
5959
5965
  approved: environmentFileSchema.optional(),
5960
5966
  // sha256 of the overlay the running container was built from (SANDBOX_ENVIRONMENT_HASH); absent = stock image.
@@ -6334,7 +6340,7 @@ export const ManifestProblemSchema = z.object({
6334
6340
  });
6335
6341
  export type ManifestProblem = z.infer<typeof ManifestProblemSchema>;
6336
6342
 
6337
- // Workspace-relative path (`.intentic/settings.json`) and everything currently wrong with that file. A file
6343
+ // Workspace-relative path (`.intentic/config/settings.json`) and everything currently wrong with that file. A file
6338
6344
  // with nothing wrong is absent from the list rather than present and empty.
6339
6345
  export const ManifestProblemReportSchema = z.object({ path: z.string(), problems: z.array(ManifestProblemSchema) });
6340
6346
  export type ManifestProblemReport = z.infer<typeof ManifestProblemReportSchema>;