chatroom-cli 1.98.7 → 1.99.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -78980,19 +78980,19 @@ function getDelegationBriefIntro() {
78980
78980
  var HANDOFF_NOT_APPLICABLE_EXACT_TEXT = "Not Applicable.";
78981
78981
 
78982
78982
  // ../../services/backend/prompts/utils/handoff-quality-principles.ts
78983
- function getHandoffQualityPrinciplesTemplateBlock() {
78983
+ function getHandoffQualityPrinciplesTemplateBlock(subject = "work") {
78984
78984
  const lines = HANDOFF_QUALITY_PRINCIPLES.flatMap((p) => [
78985
- `- **${p.name}:** <how this work demonstrates ${p.name.toLowerCase()}, or exactly "${HANDOFF_NOT_APPLICABLE_EXACT_TEXT}">`,
78985
+ `- **${p.name}:** <how this ${subject} demonstrates ${p.name.toLowerCase()}, or exactly "${HANDOFF_NOT_APPLICABLE_EXACT_TEXT}">`,
78986
78986
  `<!-- ${p.name}: ${p.description} -->`,
78987
78987
  ""
78988
78988
  ]);
78989
78989
  return lines.join(`
78990
78990
  `).trimEnd();
78991
78991
  }
78992
- function getHandoffQualityPrinciplesSectionBlock() {
78992
+ function getHandoffQualityPrinciplesSectionBlock(subject = "work") {
78993
78993
  return `${PROOF_OF_PRINCIPLES_HEADING_H2}
78994
78994
  ${PROOF_OF_PRINCIPLES_MANDATORY_COMMENT}
78995
- ${getHandoffQualityPrinciplesTemplateBlock()}`;
78995
+ ${getHandoffQualityPrinciplesTemplateBlock(subject)}`;
78996
78996
  }
78997
78997
  var HANDOFF_QUALITY_PRINCIPLES, PROOF_OF_PRINCIPLES_HEADING_H2 = "## Proof of Principles", PROOF_OF_PRINCIPLES_MANDATORY_COMMENT;
78998
78998
  var init_handoff_quality_principles = __esm(() => {
@@ -79087,163 +79087,195 @@ var init_builder_to_planner = __esm(() => {
79087
79087
  init_role_guidance_disclosure();
79088
79088
  });
79089
79089
 
79090
- // ../../services/backend/prompts/enhancer/defragmentation-reference.ts
79091
- function renderDefragmentationHandoffReference() {
79092
- return [
79093
- "### Defragmentation workflow checklist",
79094
- 'Complete the optional **Defragmentation** section when the planner check-in addresses a large or multi-surface system revision, including refactoring, consolidation, or consistency work. Write exactly "Not Applicable." only when no such revision is proposed.',
79095
- "",
79096
- "1. **Study surfaces** — map all call sites, use cases, and complexity variants before proposing slices; name every relevant file/module",
79097
- "2. **Golden implementation** — build a standalone canonical solution first; introduce canonical domain entities/types only when the studied variants require them, then shared use cases, UI components, or utilities; do not patch duplicates in place",
79098
- "3. **Migrate callers** — refactor all consumers to the golden path; each slice must be shippable end-to-end",
79099
- "4. **Delete legacy** — remove old implementations only after migration is complete; no dead-code leftovers",
79100
- "",
79101
- "### Anti-patterns to flag",
79102
- "- Incremental copy-paste fixes across N files without a golden SSOT",
79103
- "- New abstraction without studying all existing variants",
79104
- '- Leaving old code "for safety" after migration',
79105
- "- Slices that add helpers/infra without a runnable end-to-end outcome",
79106
- "- Parallel implementations coexisting without a deletion plan",
79107
- "",
79108
- "### Structural decisions",
79109
- "- Identify SSOT locations for domain entities, shared use cases, and UI components",
79110
- "- Align with `structural-decisions` glossary: folder structure, file naming, interface locations",
79111
- "- Flag when the plan scatters the canonical implementation across unrelated modules"
79112
- ].join(`
79113
- `);
79114
- }
79090
+ // ../../services/backend/src/domain/usecase/enhancer/enhancer-handoff-content.ts
79091
+ var ENHANCER_USER_MESSAGE_PLACEHOLDER = "{{USER_MESSAGE}}";
79115
79092
 
79116
- // ../../services/backend/prompts/enhancer/webapp-ux-reference.ts
79117
- function renderWebappUxHandoffReference() {
79118
- return [
79119
- "### UX review checklist",
79120
- 'Complete the optional **UX** section in your output when the planner proposes UI changes. Write exactly "Not Applicable." for non-UI tasks. Put code snippets in **Suggested edits** only.',
79121
- "",
79122
- "1. **Flows** — is the primary path straightforward? simpler alternatives exist?",
79123
- "2. **Patterns** — consistent with existing project components and conventions? recommend one when multiple exist.",
79124
- "3. **Layout** — unnecessary complexity or wrappers? layout stable across loading/empty/error transitions (no layout shift when async content arrives or state changes)?",
79125
- "4. **Shortcuts** — aligned with the project keyboard/shortcut conventions? gaps or conflicts?",
79126
- "5. **States** — loading, error, and empty states explicitly handled for async surfaces (no blank panels, silent failures, or missing retry affordances)?",
79127
- "6. **Error boundaries** — failures scoped so one subtree does not crash the whole app?",
79128
- "7. **Feedback** — timely response for async user actions?",
79129
- "8. **Interaction affordance** — clickable/interactive elements use pointer cursor (or the project's established equivalent) where applicable?",
79130
- "9. **Destructive actions** — irreversible or high-impact single actions gated by confirmation?",
79131
- "10. **Bulk actions** — batch/multi-item operations confirmed with scope or impact summary?",
79132
- "",
79133
- "### Review principles",
79134
- "- Ground feedback in the planner check-in and the project codebase — cite existing patterns rather than inventing generic UI preferences.",
79135
- "- Flag missing states, layout-shift risk, and missing interaction affordances when the plan omits them; recommend consistency with established project conventions.",
79136
- "- Do **not** prescribe style choices the project has not adopted (e.g. specific flex layouts, canonical card chrome, responsive utility patterns, button label copy).",
79137
- "- When multiple valid patterns exist in the codebase, recommend one and explain the tradeoff."
79138
- ].join(`
79139
- `);
79093
+ // ../../services/backend/prompts/utils/frontend-design-ux-checklist.ts
79094
+ function getFrontendDesignUxPlanningPrinciplesBlock() {
79095
+ return `<!-- Ground every flow in user history and repository patterns. Recommend one existing pattern when multiple exist; do not prescribe style choices the project has not adopted. -->`;
79096
+ }
79097
+ function getFrontendDesignUxFlowChecklistBlock() {
79098
+ return `**UX quality (complete for every interactive step in this flow):**
79099
+ - **States:** loading | empty | error | success — no blank panels, silent failures, or missing retry affordances
79100
+ - **Layout:** stable across async transitions — no layout shift when content arrives or state changes
79101
+ - **Patterns:** consistent with existing project components — cite the chosen pattern and repo-relative file
79102
+ - **Shortcuts:** aligned with project keyboard/shortcut conventions; document tab order and gaps
79103
+ - **Feedback:** timely response for async user actions
79104
+ - **Interaction affordance:** pointer cursor (or project equivalent) on clickable elements
79105
+ - **Safeguards:** confirmation before destructive actions; bulk operations confirmed with scope summary`;
79140
79106
  }
79141
79107
 
79142
79108
  // ../../services/backend/prompts/utils/enhancer-feedback-template-body.ts
79143
79109
  function getEnhancerFeedbackTemplateBody() {
79144
79110
  return `<handoff-overview>
79145
79111
  ## Summary
79146
- <overall assessment — cite specific strengths, risks, and whether the approach is sound; reference concrete elements from the check-in>
79112
+ <one paragraph: what we're building and the single design direction>
79147
79113
 
79148
- ## User intent alignment
79149
- <specific misreadings or missing constraints — what the user asked vs what the planner proposed>
79114
+ ## User intent and constraints
79115
+ <explicit requirements from user messages; hard constraints that must not be violated>
79150
79116
  </handoff-overview>
79151
79117
 
79152
- <!-- UI collapses proofs, direction, ux, defragmentation, and notes by default; overview and action required are expanded -->
79118
+ <!-- UI collapses proofs, direction, frontend design, data design, and notes by default; overview and action required are expanded -->
79153
79119
 
79154
79120
  <handoff-proofs>
79155
- ## Reasoning review
79156
- <specific logical errors, weak inference, or contradictions — cite the claim and why it fails>
79121
+ ## Repository evidence
79122
+ <files read, current behavior verified, patterns reused — cite repo-relative paths and short snippets only>
79123
+
79124
+ ${getHandoffQualityPrinciplesSectionBlock("design")}
79157
79125
  </handoff-proofs>
79158
79126
 
79159
79127
  <handoff-direction>
79160
- ## Alignment with eventual user handoff
79161
- <specific gaps for user-facing completeness — what proof or report sections would be missing>
79128
+ ## Recommended design
79129
+ <!-- ONE design only. No Option A/B/C. -->
79130
+
79131
+ <2–4 sentences: the chosen architecture and why it fits the request and existing system>
79162
79132
  </handoff-direction>
79163
79133
 
79164
- <handoff-ux>
79165
- <!-- Optional — write exactly "Not Applicable." when no UI changes are proposed -->
79166
- <!-- When UI is proposed: specific findings tied to the planner's proposal. No code blocks (use Suggested edits). -->
79167
- - **Flows:** <click count, nested modals, simpler alternatives>
79168
- - **Patterns:** <consistency with existing project components; recommend one when multiple>
79169
- - **Layout:** <complexity, wrappers, layout stable across state transitions — no layout shift>
79170
- - **Shortcuts:** <alignment with catalog; gaps or conflicts>
79171
- - **States:** <loading/error/empty explicitly handled; no blank panels or silent failures>
79172
- - **Error boundaries:** <error boundary placement; failure isolated from the whole app>
79173
- - **Feedback:** <timely response for async actions>
79174
- - **Interaction affordance:** <pointer cursor or project equivalent on clickable elements where applicable>
79175
- - **Destructive safeguards:** <confirmation before irreversible/high-impact actions>
79176
- - **Bulk safeguards:** <confirmation with scope summary for batch operations>
79177
- </handoff-ux>
79178
-
79179
- <handoff-defragmentation>
79180
- <!-- Optional — write exactly "Not Applicable." when no large or multi-surface system revision is proposed -->
79181
- <!-- When revision work is proposed: specific findings tied to the planner's proposal. No code blocks (use Suggested edits). -->
79182
- - **Surfaces:** <call sites and modules identified; gaps in surface mapping>
79183
- - **Golden path:** <whether planner builds standalone canonical implementation first>
79184
- - **Domain model:** <canonical types/entities needed or "not needed">
79185
- - **Shared components:** <shared abstractions planned (use cases, UI, utilities)>
79186
- - **Slice ordering:** <study → golden → migrate → delete sequence respected?>
79187
- - **Migration plan:** <how all callers move to golden path>
79188
- - **Deletion plan:** <old implementations slated for removal>
79189
- - **Duplication:** <existing duplicates to eliminate; risk of new duplication>
79190
- - **Structural decisions:** <folder/module boundaries; SSOT locations>
79191
- </handoff-defragmentation>
79134
+ <handoff-frontend-design>
79135
+ ## Frontend / user-centric design
79192
79136
 
79193
- <handoff-notes>
79194
- ## Knowledge gaps
79195
- <specific facts, files, or research to verify — name what to check and why>
79196
- </handoff-notes>
79137
+ ${getFrontendDesignUxPlanningPrinciplesBlock()}
79197
79138
 
79198
- <handoff-action>
79199
- ## Risks & failure modes
79200
- <specific risks tied to this plan — what fails, under what conditions, and how to mitigate>
79139
+ ### Flow 1: <name>
79140
+ **Entry:** User visits \`<route/page>\` from \`<source>\`.
79201
79141
 
79202
- ## Recommendations
79203
- <!-- SECOND-LAST — concrete, actionable suggestions tied to the check-in. Include tradeoffs and considerations. No code blocks here (use Suggested edits for snippets). -->
79142
+ | Step | User action | System response | UI state |
79143
+ |------|-------------|-----------------|----------|
79144
+ | 1 | User clicks \`<element label>\` | \`<navigation / modal / fetch>\` | \`<loading → success/error>\` |
79204
79145
 
79205
- ## Suggested edits (remove or change only)
79206
- <!-- LAST — proposed edits to grounding and builder-handoff. File paths and code snippets required when recommending changes. Omit entirely if none. -->
79207
- When you recommend removing or changing specific content in the planner's check-in, list each change here with file-level detail and code examples.
79208
- ${getFileReferenceGuidanceComment()}
79146
+ ${getFrontendDesignUxFlowChecklistBlock()}
79147
+
79148
+ **Expected interactions per element:**
79149
+ - \`<ElementName>\` (\`apps/webapp/src/...\`): click → \`<handler>\`; keyboard: \`<tab order>\`; disabled when \`<condition>\`
79150
+
79151
+ ### Element, style, and layout specification
79152
+
79153
+ #### \`<ComponentName>\` — \`apps/webapp/src/path/to/Component.tsx\`
79154
+ **Change:** <create | modify>
79155
+
79156
+ **Layout:** \`<div className="...">\` — flex/grid, gaps, breakpoints
79157
+
79158
+ **States:** loading | empty | error | success
79159
+
79160
+ \`\`\`tsx
79161
+ export function ComponentName({ ... }: Props) {
79162
+ // target structure — props, classNames, branches
79163
+ }
79164
+ \`\`\`
79165
+
79166
+ <!-- Repeat per component; write exactly "Not Applicable." for the entire section if no UI -->
79167
+ </handoff-frontend-design>
79168
+
79169
+ <handoff-data-design>
79170
+ ## Persistent state and query pattern design
79171
+
79172
+ **Goal:** Small updates must not cause large cache invalidations. High-frequency writes use projections to smaller tables.
79209
79173
 
79210
- ### <section or claim to remove or change>
79211
- **File:** \`apps/webapp/src/path/to/file.ts\`
79212
- **Change:** <what to remove, replace, or correct and why>
79174
+ ### 1. Sources of concern
79175
+ | Source | Write frequency | Read pattern | Risk |
79176
+ |--------|---------------|--------------|------|
79177
+ | \`<table/mutation>\` | \`<frequency>\` | \`<pattern>\` | \`<scan / hot partition>\` |
79178
+
79179
+ ### 2. Schema design
79180
+ **Hot path:** \`<table>\` — fields, projection from \`<source>\`
79181
+ **Cold path:** \`<table>\` — …
79182
+
79183
+ \`\`\`typescript
79184
+ // target schema shape
79185
+ \`\`\`
79186
+
79187
+ ### 3. Index design (within limits)
79188
+ | Table | Index | Serves query | Budget |
79189
+ |-------|-------|--------------|--------|
79190
+
79191
+ ### 4. Query design (within limits)
79192
+ | Query | Index | Rows scanned | Timeout | Invalidation scope |
79193
+ |-------|-------|--------------|---------|-------------------|
79213
79194
 
79214
79195
  \`\`\`typescript
79215
- // Code snippet: what should change, be removed, or what the planner got wrong
79196
+ // target query signature
79216
79197
  \`\`\`
79217
79198
 
79218
- (Add one ### block per distinct removal or change. Use repo-relative paths with file extensions.)
79199
+ <!-- Write exactly "Not Applicable." for the entire section if no persistence changes -->
79200
+ </handoff-data-design>
79201
+
79202
+ <handoff-notes>
79203
+ ## Open questions for user
79204
+ <decisions only the user can make, or write "Not Applicable.">
79205
+ </handoff-notes>
79206
+
79207
+ <handoff-action>
79208
+ ## Recommended implementation sequence
79209
+ <!-- Ordered slices for the planner — references files from design sections. For large or multi-surface revisions, activate the defragmentation skill before delegating. -->
79210
+
79211
+ 1. …
79212
+ 2. …
79213
+
79214
+ ## Files touched (index)
79215
+ - \`apps/webapp/src/...\` — …
79216
+ - \`services/backend/convex/...\` — …
79219
79217
  </handoff-action>`;
79220
79218
  }
79221
- var init_enhancer_feedback_template_body = () => {};
79219
+ var init_enhancer_feedback_template_body = __esm(() => {
79220
+ init_handoff_quality_principles();
79221
+ });
79222
79222
 
79223
- // ../../services/backend/prompts/teams/duo/handoff-templates/enhancer-to-planner.ts
79224
- function getEnhancerToPlannerHandoffTemplate() {
79225
- return `${getHandoffRecipientVisibilityCallout("planner")}
79223
+ // ../../services/backend/prompts/enhancer/handoff-templates.ts
79224
+ function roleLabel(role) {
79225
+ return role.charAt(0).toUpperCase() + role.slice(1).toLowerCase();
79226
+ }
79227
+ function getEntryPointToEnhancerHandoffTemplate(entryPointRole) {
79228
+ const label = roleLabel(entryPointRole);
79229
+ return `**Planning Request (${label} → Enhancer)** — after reading the user message and any pinned chatroom context, fill only <additional-context> below. The system injects the user message automatically — do not copy it.
79226
79230
 
79227
- ${getHandoffReportTemplateIntro("Planning Feedback (Enhancer → Planner)")}
79231
+ \`\`\`markdown
79232
+ <user-message>
79233
+ <!-- Injected automatically from the originating user message — do not edit or copy the user message here -->
79234
+ ${ENHANCER_USER_MESSAGE_PLACEHOLDER}
79235
+ </user-message>
79228
79236
 
79229
- The planner sent you three XML sections. Your job is **advisory adversarial review** — raise risks, challenge assumptions, align with user intent. Be **specific and targeted**: cite concrete claims, files, UX choices, and gaps from the check-in so the planner can improve the plan without re-synthesizing vague feedback.
79237
+ <additional-context>
79238
+ ## Goal
79239
+ <what the user wants in plain language — one short paragraph>
79240
+ </additional-context>
79241
+ \`\`\`
79230
79242
 
79231
- Give **concrete, actionable recommendations** in every section. End with **Recommendations** (second-last: summarized suggestions, tradeoffs, and considerations) then **Suggested edits** (last: proposed edits to grounding and the builder-handoff with file paths and code snippets). For UI work, complete the optional **UX** section using the reference below. For large or multi-surface revision work, complete the optional **Defragmentation** section using its reference below. **Do not rewrite their full builder brief.** The planner makes the final call.
79243
+ After handoff succeeds, **end your turn immediately**. The enhancer independently downloads the originating message history and returns design input as your next ${entryPointRole.toLowerCase()} task.`;
79244
+ }
79245
+ function getEnhancerToEntryPointHandoffTemplate(entryPointRole) {
79246
+ const normalizedRole = entryPointRole.toLowerCase();
79247
+ const label = roleLabel(entryPointRole);
79248
+ return `**Design Input (Enhancer → ${label})**
79232
79249
 
79233
- ${renderWebappUxHandoffReference()}
79250
+ You are the design authority for this request. Recover conversation history, inspect the repository, and return **one** complete design — not options. The ${normalizedRole} agent verifies your design and delegates implementation.
79234
79251
 
79235
- ${renderDefragmentationHandoffReference()}
79252
+ **Rules:**
79253
+ - Design first — complete frontend and data/query sections before implementation sequencing.
79254
+ - **No alternative approaches** — one \`Recommended design\` only.
79255
+ - Frontend and data sections: code granularity (component names, props, classes, file paths, schema, indexes, queries).
79256
+ - Complete per-flow **UX quality** checklist in frontend design — states, layout stability, patterns, safeguards.
79257
+ - For large or multi-surface revisions, note in implementation sequence that the entry point should activate the \`defragmentation\` skill.
79258
+ - In \`<handoff-proofs>\`, complete **Proof of Principles** for how this design satisfies each quality principle (or "Not Applicable.").
79259
+ - Write "Not Applicable." only when a major design section truly does not apply.
79236
79260
 
79237
79261
  \`\`\`markdown
79238
79262
  ${getEnhancerFeedbackTemplateBody()}
79239
79263
  \`\`\`
79240
79264
 
79241
- Return only the feedback markdown — no preamble. Follow this structure; omit sections that truly do not apply.`;
79265
+ Return only the design input markdown — no preamble. Follow this structure; use "Not Applicable." where a major section does not apply.`;
79242
79266
  }
79243
- var init_enhancer_to_planner = __esm(() => {
79267
+ var init_handoff_templates = __esm(() => {
79244
79268
  init_enhancer_feedback_template_body();
79245
79269
  });
79246
79270
 
79271
+ // ../../services/backend/prompts/teams/duo/handoff-templates/enhancer-to-planner.ts
79272
+ function getEnhancerToPlannerHandoffTemplate() {
79273
+ return getEnhancerToEntryPointHandoffTemplate("planner");
79274
+ }
79275
+ var init_enhancer_to_planner = __esm(() => {
79276
+ init_handoff_templates();
79277
+ });
79278
+
79247
79279
  // ../../services/backend/prompts/teams/duo/handoff-templates/planner-to-builder.ts
79248
79280
  function getPlannerToBuilderHandoffTemplate() {
79249
79281
  return `${getHandoffRecipientVisibilityCallout("builder")}
@@ -79322,40 +79354,11 @@ var init_planner_to_builder = () => {};
79322
79354
 
79323
79355
  // ../../services/backend/prompts/teams/duo/handoff-templates/planner-to-enhancer.ts
79324
79356
  function getPlannerToEnhancerHandoffTemplate() {
79325
- return `${getHandoffRecipientVisibilityCallout("enhancer")}
79326
-
79327
- **Mandatory Planning Check-in (Planner → Enhancer)** — paste into the handoff message. **Do not skip this check-in** when the enhancer is enabled.
79328
-
79329
- The enhancer starts from this check-in — it may read files in the repo to verify your grounding. Use the three XML sections below **exactly** — they delimit what the enhancer will review.
79330
-
79331
- \`\`\`markdown
79332
- <user-message>
79333
- <verbatim or faithful quote of the user's request — include constraints, priorities, and classification context if relevant>
79334
- </user-message>
79335
-
79336
- <grounding>
79337
- <fully detailed research — include file paths and snippets the enhancer should verify; it may read the codebase to validate your proposal>
79338
-
79339
- Include:
79340
- - **Existing code examples** — relevant snippets, patterns, and conventions already in the codebase
79341
- - **File references** — paths to every file you investigated or plan to change
79342
- - **Technology choices** — libraries, frameworks, APIs, and why they apply here
79343
- - **Observations** — what you learned, edge cases, risks, and open questions from investigation
79344
- ${getFileReferenceGuidanceComment()}
79345
- </grounding>
79346
-
79347
- <builder-handoff>
79348
- <your complete, filled-in Delegation Brief for builder review — every section with real content, not placeholders>
79349
-
79350
- Follow the **Handoff to \`builder\`** template structure (Summary, Goal, approach, Requirements, etc.). The enhancer reviews **approach and scope** — not line-by-line file edits. Keep file references for context, but expect advisory feedback on risks and gaps, not a rewritten brief.
79351
- </builder-handoff>
79352
- \`\`\`
79353
-
79354
- After handoff succeeds, **end your turn immediately** — do not monitor the enhancer or wait in this session. When review completes, enhancer → planner handoff delivers feedback as your next planner task.
79355
-
79356
- After you receive that feedback, you make the **final call** — incorporate valid critiques, ignore what doesn't apply, then proceed to \`builder\` or \`user\`. Do not re-check with the enhancer for this slice; one round only.`;
79357
+ return getEntryPointToEnhancerHandoffTemplate("planner");
79357
79358
  }
79358
- var init_planner_to_enhancer = () => {};
79359
+ var init_planner_to_enhancer = __esm(() => {
79360
+ init_handoff_templates();
79361
+ });
79359
79362
 
79360
79363
  // ../../services/backend/prompts/utils/context-disclosure.ts
79361
79364
  function getContextReadDisclosureComment(params = {}) {
@@ -79533,7 +79536,7 @@ function getDuoHandoffTemplate(query) {
79533
79536
  return getter?.(query) ?? null;
79534
79537
  }
79535
79538
  var DUO_HANDOFF_TEMPLATES;
79536
- var init_handoff_templates = __esm(() => {
79539
+ var init_handoff_templates2 = __esm(() => {
79537
79540
  init_builder_to_planner();
79538
79541
  init_enhancer_to_planner();
79539
79542
  init_planner_to_builder();
@@ -79576,9 +79579,12 @@ function getSoloHandoffTemplate(query) {
79576
79579
  return getter?.(query) ?? null;
79577
79580
  }
79578
79581
  var SOLO_HANDOFF_TEMPLATES;
79579
- var init_handoff_templates2 = __esm(() => {
79582
+ var init_handoff_templates3 = __esm(() => {
79580
79583
  init_solo_to_user();
79584
+ init_handoff_templates();
79581
79585
  SOLO_HANDOFF_TEMPLATES = {
79586
+ "solo:enhancer": () => getEntryPointToEnhancerHandoffTemplate("solo"),
79587
+ "enhancer:solo": () => getEnhancerToEntryPointHandoffTemplate("solo"),
79582
79588
  "solo:user": (query) => getSoloToUserReportTemplate({
79583
79589
  chatroomId: query.chatroomId,
79584
79590
  role: query.role,
@@ -79596,9 +79602,9 @@ function getHandoffTemplate(query) {
79596
79602
  return getSoloHandoffTemplate(query);
79597
79603
  return null;
79598
79604
  }
79599
- var init_handoff_templates3 = __esm(() => {
79600
- init_handoff_templates();
79605
+ var init_handoff_templates4 = __esm(() => {
79601
79606
  init_handoff_templates2();
79607
+ init_handoff_templates3();
79602
79608
  });
79603
79609
 
79604
79610
  // ../../services/backend/prompts/cli/handoff/view-template.ts
@@ -79615,7 +79621,7 @@ function viewHandoffTemplate(params) {
79615
79621
  return template;
79616
79622
  }
79617
79623
  var init_view_template2 = __esm(() => {
79618
- init_handoff_templates3();
79624
+ init_handoff_templates4();
79619
79625
  });
79620
79626
 
79621
79627
  // src/commands/handoff/view-template.ts
@@ -79780,9 +79786,12 @@ function formatDecodeError(error) {
79780
79786
 
79781
79787
  // ../../services/backend/prompts/native/session-continuity.ts
79782
79788
  function getNativeHandoffTurnEndGuidance(nextRole) {
79789
+ return getHandoffTurnEndGuidance(nextRole);
79790
+ }
79791
+ function getHandoffTurnEndGuidance(nextRole) {
79783
79792
  const lines = [
79784
79793
  "",
79785
- "**Handoff must be your last action this turn.** After running handoff, **End your turn now** — stop tool calls. Your session stays active."
79794
+ "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.**"
79786
79795
  ];
79787
79796
  if (nextRole.toLowerCase() === "user") {
79788
79797
  lines.push("The system delivers the next chatroom task when the user sends one.");
@@ -79792,11 +79801,11 @@ function getNativeHandoffTurnEndGuidance(nextRole) {
79792
79801
  return lines.join(`
79793
79802
  `);
79794
79803
  }
79795
- function getNativeEnhancerCheckInTurnEndGuidance() {
79804
+ function getNativeEnhancerRequestTurnEndGuidance() {
79796
79805
  return [
79797
79806
  "",
79798
- "**Handoff must be your last action this turn.** After running handoff, **End your turn now** — stop tool calls. Your session stays active.",
79799
- "Do **not** wait for enhancer feedback, poll, or monitor the enhancer job in this turn. The system delivers enhancer feedback as your next planner task when review completes."
79807
+ "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.**",
79808
+ "Do **not** wait for enhancer input, poll, or monitor the enhancer job in this turn. The system delivers independent planning input as your next task when analysis completes."
79800
79809
  ].join(`
79801
79810
  `);
79802
79811
  }
@@ -79826,10 +79835,14 @@ var init_backlog = __esm(() => {
79826
79835
  // ../../services/backend/src/domain/usecase/skills/modules/code-review/index.ts
79827
79836
  var init_code_review = () => {};
79828
79837
 
79838
+ // ../../services/backend/src/domain/usecase/skills/modules/defragmentation/index.ts
79839
+ var init_defragmentation = () => {};
79840
+
79829
79841
  // ../../services/backend/src/domain/usecase/skills/registry.ts
79830
79842
  var init_registry4 = __esm(() => {
79831
79843
  init_backlog();
79832
79844
  init_code_review();
79845
+ init_defragmentation();
79833
79846
  });
79834
79847
 
79835
79848
  // ../../services/backend/prompts/sections/glossary.ts
@@ -79866,7 +79879,8 @@ var init_glossary = __esm(() => {
79866
79879
  },
79867
79880
  {
79868
79881
  term: "structural-decisions",
79869
- definition: "Meta-level architectural choices that persist in the codebase and influence consistency: " + "folder structure, file naming, interface definitions, and key abstraction names/locations " + "(e.g., Repository/Service layers)."
79882
+ definition: "Meta-level architectural choices that persist in the codebase and influence consistency: " + "folder structure, file naming, interface definitions, and key abstraction names/locations " + "(e.g., Repository/Service layers).",
79883
+ linkedSkillId: "defragmentation"
79870
79884
  }
79871
79885
  ];
79872
79886
  NATIVE_GLOSSARY_TERMS = [
@@ -80225,15 +80239,15 @@ var init_types = __esm(() => {
80225
80239
  // ../../services/backend/prompts/review-guidelines/index.ts
80226
80240
  var init_review_guidelines = () => {};
80227
80241
  // ../../services/backend/prompts/generator.ts
80228
- function isEnhancerCheckInQueuedHandoff(params) {
80229
- return params.enhancerCheckInQueued === true && params.role.toLowerCase() === "planner" && params.nextRole.toLowerCase() === "enhancer";
80242
+ function isEnhancerRequestQueuedHandoff(params) {
80243
+ return params.enhancerRequestQueued === true && params.nextRole.toLowerCase() === "enhancer";
80230
80244
  }
80231
- function getEnhancerCheckInQueuedConfirmationLines(nativeIntegration) {
80232
- const turnEndRule = nativeIntegration ? "**End your turn now** — do not wait for feedback, poll, monitor the enhancer, or re-submit the handoff. The system delivers enhancer feedback as your next planner task when review completes." : "**Run get-next-task now and end your turn** — do not wait for feedback, poll, monitor the enhancer, or re-submit the handoff.";
80245
+ function getEnhancerRequestQueuedConfirmationLines(nativeIntegration) {
80246
+ const turnEndRule = nativeIntegration ? "**Handoff complete. End your turn now — stop tool calls. The system will send you a message when further action is required.** Do **not** wait for enhancer input, poll, monitor the enhancer, or re-submit the handoff. The system delivers independent planning input as your next task when analysis completes." : "**Run get-next-task now and end your turn — stop tool calls. The system will send you a message when further action is required.** Do **not** wait for enhancer input, poll, monitor the enhancer, or re-submit the handoff.";
80233
80247
  return [
80234
- "✅ Planning check-in queued for handoff enhancer",
80248
+ "✅ User request queued for handoff enhancer",
80235
80249
  "",
80236
- "Your check-in was sent to the handoff enhancer (async). You will receive planning feedback back as a planner task when review completes.",
80250
+ "The user request was sent to the handoff enhancer (async). You will receive independent planning input as your next task when analysis completes.",
80237
80251
  turnEndRule
80238
80252
  ];
80239
80253
  }
@@ -80244,24 +80258,28 @@ function generateHandoffOutput(params) {
80244
80258
  chatroomId,
80245
80259
  convexUrl,
80246
80260
  supportsNativeIntegration,
80247
- enhancerCheckInQueued
80261
+ enhancerRequestQueued
80248
80262
  } = params;
80249
80263
  const cliEnvPrefix = getCliEnvPrefix(convexUrl);
80250
- const enhancerCheckIn = isEnhancerCheckInQueuedHandoff({ role, nextRole, enhancerCheckInQueued });
80264
+ const enhancerRequest = isEnhancerRequestQueuedHandoff({
80265
+ nextRole,
80266
+ enhancerRequestQueued
80267
+ });
80251
80268
  const lines = [];
80252
- if (enhancerCheckIn) {
80253
- lines.push(...getEnhancerCheckInQueuedConfirmationLines(supportsNativeIntegration));
80269
+ if (enhancerRequest) {
80270
+ lines.push(...getEnhancerRequestQueuedConfirmationLines(supportsNativeIntegration));
80254
80271
  } else {
80255
80272
  lines.push(`✅ Chatroom task completed and handed off to ${nextRole}`);
80256
80273
  }
80257
80274
  if (supportsNativeIntegration) {
80258
- lines.push(enhancerCheckIn ? getNativeEnhancerCheckInTurnEndGuidance() : getNativeHandoffTurnEndGuidance(nextRole));
80275
+ lines.push(enhancerRequest ? getNativeEnhancerRequestTurnEndGuidance() : getNativeHandoffTurnEndGuidance(nextRole));
80259
80276
  } else {
80260
80277
  lines.push("");
80261
80278
  lines.push("✅ Level B complete (chatroom task handed off).");
80262
80279
  lines.push("⏳ Level A continues (session is still active) — run get-next-task to stay connected:");
80263
80280
  lines.push("");
80264
80281
  lines.push(`\`${getNextTaskCommand({ chatroomId, role, cliEnvPrefix })}\``);
80282
+ lines.push(getHandoffTurnEndGuidance(nextRole));
80265
80283
  }
80266
80284
  return lines.join(`
80267
80285
  `);
@@ -80419,7 +80437,7 @@ var handoffEffect = (chatroomId, options) => exports_Effect.gen(function* () {
80419
80437
  chatroomId,
80420
80438
  convexUrl,
80421
80439
  supportsNativeIntegration: result.supportsNativeIntegration,
80422
- enhancerCheckInQueued: result.enhancerJobId != null
80440
+ enhancerRequestQueued: result.enhancerJobId != null
80423
80441
  }));
80424
80442
  });
80425
80443
  });
@@ -83515,6 +83533,321 @@ function harnessCapabilitiesFingerprint(harnesses, versions) {
83515
83533
  return `${h}::${v3}`;
83516
83534
  }
83517
83535
 
83536
+ // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-schema.ts
83537
+ var DURABLE_FIFO_QUEUE_MIGRATIONS;
83538
+ var init_durable_fifo_queue_schema = __esm(() => {
83539
+ DURABLE_FIFO_QUEUE_MIGRATIONS = [
83540
+ `CREATE TABLE IF NOT EXISTS schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`,
83541
+ `CREATE TABLE IF NOT EXISTS fifo_outbox_entries (id INTEGER PRIMARY KEY AUTOINCREMENT, delivery_key TEXT NOT NULL, payload_json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', attempts INTEGER NOT NULL DEFAULT 0, last_error TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
83542
+ `CREATE INDEX IF NOT EXISTS idx_fifo_outbox_pending ON fifo_outbox_entries(delivery_key, status, id)`
83543
+ ];
83544
+ });
83545
+
83546
+ // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-store.ts
83547
+ import { DatabaseSync } from "node:sqlite";
83548
+ import { mkdirSync as mkdirSync4 } from "node:fs";
83549
+ import { dirname as dirname9 } from "node:path";
83550
+ function openDurableFifoQueueStore(dbPath) {
83551
+ mkdirSync4(dirname9(dbPath), { recursive: true });
83552
+ const db = new DatabaseSync(dbPath);
83553
+ for (const sql of DURABLE_FIFO_QUEUE_MIGRATIONS)
83554
+ db.exec(sql);
83555
+ db.prepare("UPDATE fifo_outbox_entries SET status='pending', updated_at=? WHERE status='in_flight'").run(Date.now());
83556
+ return {
83557
+ enqueue: (key, payloadJson) => {
83558
+ const now = Date.now();
83559
+ return Number(db.prepare("INSERT INTO fifo_outbox_entries(delivery_key,payload_json,created_at,updated_at) VALUES(?,?,?,?) RETURNING id").get(key, payloadJson, now, now).id);
83560
+ },
83561
+ claimNextBatch: (key, limit) => db.prepare("UPDATE fifo_outbox_entries SET status='in_flight', attempts=attempts+1, updated_at=? WHERE id IN (SELECT id FROM fifo_outbox_entries WHERE delivery_key=? AND status='pending' ORDER BY id LIMIT ?) RETURNING id, delivery_key as deliveryKey, payload_json as payloadJson, attempts").all(Date.now(), key, limit),
83562
+ markDone: (id3) => {
83563
+ db.prepare("DELETE FROM fifo_outbox_entries WHERE id=?").run(id3);
83564
+ },
83565
+ markPending: (id3) => {
83566
+ db.prepare("UPDATE fifo_outbox_entries SET status='pending', last_error=NULL, updated_at=? WHERE id=?").run(Date.now(), id3);
83567
+ },
83568
+ updatePayload: (id3, payloadJson) => {
83569
+ db.prepare("UPDATE fifo_outbox_entries SET payload_json=?, updated_at=? WHERE id=?").run(payloadJson, Date.now(), id3);
83570
+ },
83571
+ markPendingRetry: (id3, error) => {
83572
+ db.prepare("UPDATE fifo_outbox_entries SET status='pending', last_error=?, updated_at=? WHERE id=?").run(String(error), Date.now(), id3);
83573
+ },
83574
+ listPendingForRecovery: (key) => db.prepare("SELECT id, delivery_key as deliveryKey, payload_json as payloadJson, attempts FROM fifo_outbox_entries WHERE delivery_key=? AND status IN ('pending','in_flight') ORDER BY id").all(key),
83575
+ close: () => db.close()
83576
+ };
83577
+ }
83578
+ var init_durable_fifo_queue_store = __esm(() => {
83579
+ init_durable_fifo_queue_schema();
83580
+ });
83581
+
83582
+ // src/daemon/infrastructure/outbox/lib/outbox-retry-backoff.ts
83583
+ function computeExponentialRetryDelayMs(attempt, retryDelayMs, maxRetryDelayMs) {
83584
+ const exponent = Math.min(Math.max(0, attempt), MAX_RETRY_EXPONENT);
83585
+ return Math.min(retryDelayMs * 2 ** exponent, maxRetryDelayMs);
83586
+ }
83587
+ var MAX_RETRY_EXPONENT = 20;
83588
+
83589
+ // src/daemon/infrastructure/outbox/lib/fifo-batched-outbox.ts
83590
+ function createFifoBatchedOutbox(o) {
83591
+ const waiters = new Map;
83592
+ let running3;
83593
+ let stopped = false;
83594
+ let retryTimer;
83595
+ let retryAttempt = 0;
83596
+ const retryDelay = () => computeExponentialRetryDelayMs(retryAttempt++, o.retryDelayMs ?? 500, o.maxRetryDelayMs ?? 5000);
83597
+ const scheduleRetry = () => {
83598
+ if (!stopped && !retryTimer) {
83599
+ retryTimer = setTimeout(() => {
83600
+ retryTimer = undefined;
83601
+ drain().catch(() => {
83602
+ return;
83603
+ });
83604
+ }, retryDelay());
83605
+ retryTimer.unref?.();
83606
+ }
83607
+ };
83608
+ const drain = async () => {
83609
+ if (running3)
83610
+ return running3;
83611
+ running3 = (async () => {
83612
+ while (true) {
83613
+ const rows = o.store.claimNextBatch(o.deliveryKey, o.batchSize);
83614
+ if (!rows.length)
83615
+ break;
83616
+ try {
83617
+ const results = await o.send(rows.map((r) => o.deserialize(r.payloadJson)));
83618
+ let hasRetry = false;
83619
+ for (let i2 = 0;i2 < results.length; i2++) {
83620
+ const item = o.deserialize(rows[i2].payloadJson);
83621
+ const outcome = o.classifyOutcome?.(results[i2], item) ?? { kind: "success" };
83622
+ if (outcome.kind === "retry") {
83623
+ o.store.updatePayload(rows[i2].id, o.serialize(outcome.item));
83624
+ o.store.markPending(rows[i2].id);
83625
+ hasRetry = true;
83626
+ continue;
83627
+ }
83628
+ o.store.markDone(rows[i2].id);
83629
+ waiters.get(rows[i2].id)?.resolve(results[i2]);
83630
+ waiters.delete(rows[i2].id);
83631
+ }
83632
+ for (let i2 = results.length;i2 < rows.length; i2++)
83633
+ o.store.markPending(rows[i2].id);
83634
+ if (hasRetry) {
83635
+ scheduleRetry();
83636
+ break;
83637
+ }
83638
+ retryAttempt = 0;
83639
+ } catch (e) {
83640
+ o.onError?.(e);
83641
+ for (const r of rows)
83642
+ o.store.markPendingRetry(r.id, e);
83643
+ scheduleRetry();
83644
+ break;
83645
+ }
83646
+ }
83647
+ })().finally(() => {
83648
+ running3 = undefined;
83649
+ });
83650
+ return running3;
83651
+ };
83652
+ const enqueue = (item) => {
83653
+ if (stopped)
83654
+ return Promise.reject(new Error("Outbox is stopped"));
83655
+ const id3 = o.store.enqueue(o.deliveryKey, o.serialize(item));
83656
+ const p = new Promise((resolve4, reject) => waiters.set(id3, { resolve: resolve4, reject }));
83657
+ drain().catch(() => {
83658
+ return;
83659
+ });
83660
+ return p;
83661
+ };
83662
+ const flushNow = async () => {
83663
+ while (o.store.listPendingForRecovery(o.deliveryKey).length) {
83664
+ await drain();
83665
+ }
83666
+ };
83667
+ const stop4 = async () => {
83668
+ if (stopped)
83669
+ return;
83670
+ if (retryTimer)
83671
+ clearTimeout(retryTimer);
83672
+ retryTimer = undefined;
83673
+ await flushNow();
83674
+ stopped = true;
83675
+ };
83676
+ return { enqueue, flushNow, stop: stop4 };
83677
+ }
83678
+ var init_fifo_batched_outbox = () => {};
83679
+
83680
+ // src/daemon/infrastructure/outbox/lib/keyed-fifo-batched-outbox-registry.ts
83681
+ function createKeyedFifoBatchedOutboxRegistry(o) {
83682
+ const boxes = new Map;
83683
+ const get13 = (key) => {
83684
+ let b = boxes.get(key);
83685
+ if (!b) {
83686
+ b = createFifoBatchedOutbox({
83687
+ batchSize: o.batchSize,
83688
+ send: o.createSend(key),
83689
+ store: o.store,
83690
+ deliveryKey: key,
83691
+ serialize: o.serialize,
83692
+ deserialize: o.deserialize,
83693
+ retryDelayMs: o.retryDelayMs,
83694
+ maxRetryDelayMs: o.maxRetryDelayMs,
83695
+ onError: (e) => o.onError?.(key, e),
83696
+ classifyOutcome: o.classifyOutcome
83697
+ });
83698
+ boxes.set(key, b);
83699
+ }
83700
+ return b;
83701
+ };
83702
+ const stop4 = async (k) => {
83703
+ const b = boxes.get(k);
83704
+ if (b) {
83705
+ await b.stop();
83706
+ boxes.delete(k);
83707
+ }
83708
+ };
83709
+ return {
83710
+ enqueue: (k, i2) => get13(k).enqueue(i2),
83711
+ flushNow: async (k) => {
83712
+ if (k)
83713
+ await get13(k).flushNow();
83714
+ else
83715
+ for (const b of boxes.values())
83716
+ await b.flushNow();
83717
+ },
83718
+ stop: stop4,
83719
+ stopAll: async () => {
83720
+ for (const k of [...boxes.keys()])
83721
+ await stop4(k);
83722
+ o.store.close();
83723
+ }
83724
+ };
83725
+ }
83726
+ var init_keyed_fifo_batched_outbox_registry = __esm(() => {
83727
+ init_fifo_batched_outbox();
83728
+ });
83729
+
83730
+ // src/daemon/infrastructure/outbox/outbox-db-path.ts
83731
+ import { homedir as homedir5 } from "node:os";
83732
+ import { join as join17 } from "node:path";
83733
+ function resolveOutboxDbPath(machineId, kind) {
83734
+ return join17(homedir5(), ".chatroom", "daemon", machineId, `outbox-${kind}.sqlite`);
83735
+ }
83736
+ var init_outbox_db_path = () => {};
83737
+
83738
+ // src/daemon/domain/entities/agent-lifecycle-fact.ts
83739
+ function buildExitedLifecycleFact(auditArgs, emittedAt) {
83740
+ const { sessionId: _sessionId, machineId: _machineId, ...fields } = auditArgs;
83741
+ return {
83742
+ kind: "exited",
83743
+ ...fields,
83744
+ revisionKey: buildAgentLifecycleRevisionKey("exited", {
83745
+ chatroomId: auditArgs.chatroomId,
83746
+ role: auditArgs.role,
83747
+ pid: auditArgs.pid,
83748
+ emittedAt
83749
+ }),
83750
+ emittedAt
83751
+ };
83752
+ }
83753
+ function normalizeAgentLifecycleFact(raw) {
83754
+ if (!raw || typeof raw !== "object") {
83755
+ throw new Error("Invalid agent lifecycle fact");
83756
+ }
83757
+ const value = raw;
83758
+ const { sessionId: _sessionId, machineId: _machineId, ...rest } = value;
83759
+ const kind = rest.kind;
83760
+ if (kind === "cleared_all_pids") {
83761
+ return {
83762
+ kind: "cleared_all_pids",
83763
+ revisionKey: String(rest.revisionKey),
83764
+ emittedAt: Number(rest.emittedAt)
83765
+ };
83766
+ }
83767
+ if (kind === "spawned" || kind === "exited") {
83768
+ return rest;
83769
+ }
83770
+ throw new Error(`Unknown agent lifecycle fact kind: ${String(kind)}`);
83771
+ }
83772
+ function agentLifecycleDeliveryKey(machineId, fact) {
83773
+ if (fact.kind === "cleared_all_pids")
83774
+ return `${machineId}:__machine__`;
83775
+ return `${machineId}:${fact.chatroomId}:${fact.role.toLowerCase()}`;
83776
+ }
83777
+ function buildAgentLifecycleRevisionKey(prefix, parts2) {
83778
+ return `${prefix}:${Object.entries(parts2).filter(([, v3]) => v3 !== undefined).map(([k, v3]) => `${k}=${v3}`).join(":")}`;
83779
+ }
83780
+
83781
+ // src/daemon/infrastructure/outbox/agent-lifecycle-outbox.ts
83782
+ function createAgentLifecycleOutboxRegistry(machineId, createSend, options) {
83783
+ const store = openDurableFifoQueueStore(resolveOutboxDbPath(machineId, "agent-lifecycle"));
83784
+ return createKeyedFifoBatchedOutboxRegistry({
83785
+ store,
83786
+ batchSize: AGENT_LIFECYCLE_OUTBOX_BATCH_SIZE,
83787
+ createSend: (key) => async (facts) => {
83788
+ const results = [];
83789
+ for (const fact of facts)
83790
+ results.push(await createSend(key)(fact));
83791
+ return results;
83792
+ },
83793
+ serialize: JSON.stringify,
83794
+ deserialize: (serialized) => normalizeAgentLifecycleFact(JSON.parse(serialized)),
83795
+ retryDelayMs: 500,
83796
+ maxRetryDelayMs: 5 * 60000,
83797
+ onError: options?.onError,
83798
+ classifyOutcome: () => ({ kind: "success" })
83799
+ });
83800
+ }
83801
+ function agentLifecycleKey(machineId, fact) {
83802
+ return agentLifecycleDeliveryKey(machineId, fact);
83803
+ }
83804
+ var AGENT_LIFECYCLE_OUTBOX_BATCH_SIZE = 1;
83805
+ var init_agent_lifecycle_outbox = __esm(() => {
83806
+ init_durable_fifo_queue_store();
83807
+ init_keyed_fifo_batched_outbox_registry();
83808
+ init_outbox_db_path();
83809
+ });
83810
+
83811
+ // src/daemon/infrastructure/outbox/agent-lifecycle-send.ts
83812
+ function toConvexLifecycleFact(fact) {
83813
+ const normalized = normalizeAgentLifecycleFact(fact);
83814
+ if (normalized.kind === "cleared_all_pids")
83815
+ return normalized;
83816
+ return {
83817
+ ...normalized,
83818
+ chatroomId: normalized.chatroomId
83819
+ };
83820
+ }
83821
+ function createAgentLifecycleSend(session2) {
83822
+ return async (fact) => {
83823
+ const args2 = {
83824
+ sessionId: session2.sessionId,
83825
+ machineId: session2.machineId,
83826
+ fact: toConvexLifecycleFact(fact)
83827
+ };
83828
+ return await session2.backend.mutation(api.machines.projectAgentLifecycleFact, args2);
83829
+ };
83830
+ }
83831
+ var init_agent_lifecycle_send = __esm(() => {
83832
+ init_api3();
83833
+ });
83834
+
83835
+ // src/daemon/entry/agent-lifecycle-outbox-runtime.ts
83836
+ function createAgentLifecycleOutboxForSession(args2) {
83837
+ return createAgentLifecycleOutboxRegistry(args2.machineId, () => createAgentLifecycleSend({
83838
+ sessionId: args2.sessionId,
83839
+ machineId: args2.machineId,
83840
+ backend: args2.backend
83841
+ }));
83842
+ }
83843
+ function enqueueAgentLifecycleFact(registry2, machineId, fact) {
83844
+ return registry2.enqueue(agentLifecycleKey(machineId, fact), fact);
83845
+ }
83846
+ var init_agent_lifecycle_outbox_runtime = __esm(() => {
83847
+ init_agent_lifecycle_outbox();
83848
+ init_agent_lifecycle_send();
83849
+ });
83850
+
83518
83851
  // src/daemon/entry/daemon-services.ts
83519
83852
  function DaemonMutableStateServiceLive(init2) {
83520
83853
  return exports_Layer.effect(DaemonMutableStateService, exports_Effect.gen(function* () {
@@ -83526,7 +83859,10 @@ function DaemonMutableStateServiceLive(init2) {
83526
83859
  };
83527
83860
  }));
83528
83861
  }
83529
- var DaemonMachineService, DaemonMachineServiceLive = (ops) => exports_Layer.succeed(DaemonMachineService, {
83862
+ var AgentLifecycleOutboxService, AgentLifecycleOutboxServiceLive = (registry2, machineId) => exports_Layer.succeed(AgentLifecycleOutboxService, {
83863
+ enqueue: (fact) => exports_Effect.promise(() => enqueueAgentLifecycleFact(registry2, machineId, fact)),
83864
+ stopAll: () => exports_Effect.promise(() => registry2.stopAll())
83865
+ }), DaemonMachineService, DaemonMachineServiceLive = (ops) => exports_Layer.succeed(DaemonMachineService, {
83530
83866
  clearAgentPid: (machineId, chatroomId, role) => exports_Effect.promise(() => ops.clearAgentPid(machineId, chatroomId, role)),
83531
83867
  persistAgentPid: (machineId, chatroomId, role, pid, harness) => exports_Effect.promise(() => ops.persistAgentPid(machineId, chatroomId, role, pid, harness)),
83532
83868
  listAgentEntries: (machineId) => exports_Effect.promise(() => ops.listAgentEntries(machineId)),
@@ -83550,6 +83886,9 @@ var DaemonMachineService, DaemonMachineServiceLive = (ops) => exports_Layer.succ
83550
83886
  }), DaemonSessionService, DaemonMutableStateService;
83551
83887
  var init_daemon_services = __esm(() => {
83552
83888
  init_esm();
83889
+ init_agent_lifecycle_outbox_runtime();
83890
+ AgentLifecycleOutboxService = class AgentLifecycleOutboxService extends exports_Context.Tag("AgentLifecycleOutboxService")() {
83891
+ };
83553
83892
  DaemonMachineService = class DaemonMachineService extends exports_Context.Tag("DaemonMachineService")() {
83554
83893
  };
83555
83894
  DaemonSpawningService = class DaemonSpawningService extends exports_Context.Tag("DaemonSpawningService")() {
@@ -83572,7 +83911,7 @@ function daemonSessionToLayers(init2) {
83572
83911
  return exports_Layer.mergeAll(BackendServiceLive({
83573
83912
  mutation: (e, a) => init2.backend.mutation(e, a),
83574
83913
  query: (e, a) => init2.backend.query(e, a)
83575
- }), ProcessServiceLive, ClockServiceLive, FsServiceLive, DaemonMachineServiceLive(init2.machine), DaemonSpawningServiceLive(init2.spawning), DaemonAgentProcessManagerServiceLive(init2.agentProcessManager), DaemonMutableStateServiceLive({
83914
+ }), ProcessServiceLive, ClockServiceLive, FsServiceLive, DaemonMachineServiceLive(init2.machine), AgentLifecycleOutboxServiceLive(init2.agentLifecycleOutbox, init2.machineId), DaemonSpawningServiceLive(init2.spawning), DaemonAgentProcessManagerServiceLive(init2.agentProcessManager), DaemonMutableStateServiceLive({
83576
83915
  lastPushedGitState: init2.lastPushedGitState,
83577
83916
  lastPushedModels: init2.lastPushedModels,
83578
83917
  lastPushedHarnessFingerprint: init2.lastPushedHarnessFingerprint,
@@ -83696,10 +84035,18 @@ var init_register_listeners = __esm(() => {
83696
84035
 
83697
84036
  // src/daemon/entry/handlers/daemon-restart-cleanup.ts
83698
84037
  var clearStaleSpawnedPidsEffect = () => exports_Effect.gen(function* () {
83699
- const backend2 = yield* BackendService;
84038
+ const outbox = yield* AgentLifecycleOutboxService;
83700
84039
  const session2 = yield* DaemonSessionService;
83701
- const result = yield* backend2.mutation(api.machines.clearAllSpawnedPids, { sessionId: session2.sessionId, machineId: session2.machineId });
83702
- return result.clearedCount;
84040
+ const emittedAt = Date.now();
84041
+ const result = yield* outbox.enqueue({
84042
+ kind: "cleared_all_pids",
84043
+ revisionKey: buildAgentLifecycleRevisionKey("cleared_all", {
84044
+ machineId: session2.machineId,
84045
+ emittedAt
84046
+ }),
84047
+ emittedAt
84048
+ });
84049
+ return result.clearedCount ?? 0;
83703
84050
  }), reapOrphanCommandRunsEffect = () => exports_Effect.gen(function* () {
83704
84051
  const backend2 = yield* BackendService;
83705
84052
  const session2 = yield* DaemonSessionService;
@@ -83737,26 +84084,26 @@ import { createHash as createHash2 } from "node:crypto";
83737
84084
  import {
83738
84085
  appendFileSync,
83739
84086
  existsSync as existsSync6,
83740
- mkdirSync as mkdirSync4,
84087
+ mkdirSync as mkdirSync5,
83741
84088
  readFileSync as readFileSync8,
83742
84089
  renameSync,
83743
84090
  unlinkSync,
83744
84091
  writeFileSync as writeFileSync3
83745
84092
  } from "node:fs";
83746
- import { homedir as homedir5 } from "node:os";
83747
- import { join as join17 } from "node:path";
84093
+ import { homedir as homedir6 } from "node:os";
84094
+ import { join as join18 } from "node:path";
83748
84095
  function getUrlHash() {
83749
84096
  const url2 = getConvexUrl();
83750
84097
  return createHash2("sha256").update(url2).digest("hex").substring(0, 8);
83751
84098
  }
83752
84099
  function getChildPidsFilePath() {
83753
- const dir = join17(homedir5(), ".chatroom");
83754
- return join17(dir, `daemon-children-${getUrlHash()}.pids`);
84100
+ const dir = join18(homedir6(), ".chatroom");
84101
+ return join18(dir, `daemon-children-${getUrlHash()}.pids`);
83755
84102
  }
83756
84103
  function ensureChatroomDir2() {
83757
- const dir = join17(homedir5(), ".chatroom");
84104
+ const dir = join18(homedir6(), ".chatroom");
83758
84105
  if (!existsSync6(dir)) {
83759
- mkdirSync4(dir, { recursive: true, mode: 448 });
84106
+ mkdirSync5(dir, { recursive: true, mode: 448 });
83760
84107
  }
83761
84108
  }
83762
84109
  function readPids() {
@@ -83869,7 +84216,7 @@ var init_orphan_tracker = __esm(() => {
83869
84216
  // src/daemon/entry/handlers/process/output-store.ts
83870
84217
  import { appendFile as appendFile2, mkdir as mkdir8, readFile as readFile6, rm as rm2 } from "node:fs/promises";
83871
84218
  import { tmpdir } from "node:os";
83872
- import { join as join18 } from "node:path";
84219
+ import { join as join19 } from "node:path";
83873
84220
 
83874
84221
  class TempFileOutputStore {
83875
84222
  state;
@@ -83932,7 +84279,7 @@ function createOutputStore(runId) {
83932
84279
  if (!RUN_ID_RE.test(runId)) {
83933
84280
  throw new Error(`Invalid runId: ${runId}`);
83934
84281
  }
83935
- const filePath = join18(TEMP_DIR, `${runId}.log`);
84282
+ const filePath = join19(TEMP_DIR, `${runId}.log`);
83936
84283
  return new TempFileOutputStore(filePath);
83937
84284
  }
83938
84285
  async function ensureTempDir() {
@@ -83946,7 +84293,7 @@ async function cleanOrphanTempFiles() {
83946
84293
  var TAIL_WINDOW_BYTES, TEMP_DIR, RUN_ID_RE, MAX_TAIL_LINES_V2 = 50;
83947
84294
  var init_output_store = __esm(() => {
83948
84295
  TAIL_WINDOW_BYTES = 32 * 1024;
83949
- TEMP_DIR = join18(tmpdir(), "chatroom-cli", "runs");
84296
+ TEMP_DIR = join19(tmpdir(), "chatroom-cli", "runs");
83950
84297
  RUN_ID_RE = /^[a-z0-9]+$/i;
83951
84298
  });
83952
84299
 
@@ -84014,74 +84361,6 @@ async function logDaemonAuditEvent(logEvent, event) {
84014
84361
  });
84015
84362
  }
84016
84363
 
84017
- // src/daemon/infrastructure/inbox/task-snapshot-state.ts
84018
- function snapshotKey(taskId, role) {
84019
- return `${taskId}:${role.toLowerCase()}`;
84020
- }
84021
-
84022
- class MachineTaskSnapshotState {
84023
- snapshots = new Map;
84024
- replace(snapshots) {
84025
- this.snapshots.clear();
84026
- this.upsert(snapshots);
84027
- }
84028
- applySignalPage(signals, snapshots) {
84029
- const snapshotsByKey = new Map(snapshots.map((snapshot) => [
84030
- snapshotKey(snapshot.taskId, snapshot.agentConfig.role),
84031
- snapshot
84032
- ]));
84033
- for (const signal of signals) {
84034
- const key = snapshotKey(signal.taskId, signal.targetRole);
84035
- const snapshot = snapshotsByKey.get(key);
84036
- if (snapshot) {
84037
- this.snapshots.set(key, snapshot);
84038
- } else {
84039
- this.snapshots.delete(key);
84040
- }
84041
- }
84042
- }
84043
- listForRole(chatroomId, role) {
84044
- const roleLower = role.toLowerCase();
84045
- return [...this.snapshots.values()].filter((snapshot) => snapshot.chatroomId === chatroomId && snapshot.agentConfig.role.toLowerCase() === roleLower);
84046
- }
84047
- listAll() {
84048
- return [...this.snapshots.values()];
84049
- }
84050
- setDesiredState(chatroomId, role, desiredState) {
84051
- const roleLower = role.toLowerCase();
84052
- for (const [key, snapshot] of this.snapshots) {
84053
- if (snapshot.chatroomId !== chatroomId || snapshot.agentConfig.role.toLowerCase() !== roleLower) {
84054
- continue;
84055
- }
84056
- this.snapshots.set(key, {
84057
- ...snapshot,
84058
- agentConfig: { ...snapshot.agentConfig, desiredState }
84059
- });
84060
- }
84061
- }
84062
- upsert(snapshots) {
84063
- for (const snapshot of snapshots) {
84064
- this.snapshots.set(snapshotKey(snapshot.taskId, snapshot.agentConfig.role), snapshot);
84065
- }
84066
- }
84067
- }
84068
-
84069
- // src/daemon/entry/native-delivery/native-delivery-session-registry.ts
84070
- function registerNativeDeliverySession(ctx) {
84071
- registered = {
84072
- ...ctx,
84073
- taskSnapshotState: ctx.taskSnapshotState ?? new MachineTaskSnapshotState
84074
- };
84075
- }
84076
- function unregisterNativeDeliverySession() {
84077
- registered = null;
84078
- }
84079
- function getNativeDeliverySession() {
84080
- return registered;
84081
- }
84082
- var registered = null;
84083
- var init_native_delivery_session_registry = () => {};
84084
-
84085
84364
  // ../../services/backend/config/reliability.ts
84086
84365
  var HARNESS_SESSION_READY_TIMEOUT_MS = 5000, DAEMON_HEARTBEAT_INTERVAL_MS, DAEMON_HEARTBEAT_TTL_MS, AGENT_REQUEST_DEADLINE_MS = 120000, OBSERVED_FULL_PUSH_INTERVAL_MS, OBSERVED_SYNC_RECONCILE_MS, WORKSPACE_RECENCY_WINDOW_MS, WORKSPACE_LIST_RECONCILE_MS, CONNECTION_CLOSE_REQUEST_TTL_MS, ENHANCER_TERMINAL_JOB_RETENTION_MS;
84087
84366
  var init_reliability = __esm(() => {
@@ -98811,8 +99090,6 @@ var init_assigned_task_snapshot_contract = __esm(() => {
98811
99090
  assignedTo: exports_external.string().optional(),
98812
99091
  lastSeenAction: exports_external.string().nullable().optional(),
98813
99092
  lastStatus: exports_external.string().nullable().optional(),
98814
- spawnedAgentPid: exports_external.number().optional(),
98815
- desiredState: agentDesiredStateSchema.optional(),
98816
99093
  sessionAugmentation: sessionAugmentationSchema.optional()
98817
99094
  };
98818
99095
  assignedTaskSignalSchema = exports_external.object(assignedTaskSignalBootstrapFields);
@@ -98889,13 +99166,99 @@ function getNativeDeliveryLedger() {
98889
99166
  }
98890
99167
  var sharedLedger;
98891
99168
 
99169
+ // src/daemon/infrastructure/agent-operational/agent-operational-read-model.ts
99170
+ function isOperationalDesiredRunning(row) {
99171
+ return row?.operationalState === "running" || row?.operationalState === "starting";
99172
+ }
99173
+
99174
+ class AgentOperationalReadModel {
99175
+ rows = new Map;
99176
+ replace(rows) {
99177
+ const changed = [];
99178
+ const nextKeys = new Set;
99179
+ for (const row of rows) {
99180
+ const key = roleKey(row.chatroomId, row.role);
99181
+ nextKeys.add(key);
99182
+ const prev = this.rows.get(key);
99183
+ if (!prev || prev.revisionKey !== row.revisionKey)
99184
+ changed.push({ chatroomId: row.chatroomId, role: row.role });
99185
+ this.rows.set(key, row);
99186
+ }
99187
+ for (const [key, prev] of this.rows) {
99188
+ if (!nextKeys.has(key)) {
99189
+ changed.push({ chatroomId: prev.chatroomId, role: prev.role });
99190
+ this.rows.delete(key);
99191
+ }
99192
+ }
99193
+ return changed;
99194
+ }
99195
+ get(chatroomId, role) {
99196
+ return this.rows.get(roleKey(chatroomId, role));
99197
+ }
99198
+ }
99199
+ var roleKey = (chatroomId, role) => `${chatroomId}:${role.toLowerCase()}`;
99200
+
99201
+ // src/daemon/infrastructure/inbox/task-snapshot-state.ts
99202
+ function snapshotKey(taskId, role) {
99203
+ return `${taskId}:${role.toLowerCase()}`;
99204
+ }
99205
+
99206
+ class MachineTaskSnapshotState {
99207
+ snapshots = new Map;
99208
+ replace(snapshots) {
99209
+ this.snapshots.clear();
99210
+ this.upsert(snapshots);
99211
+ }
99212
+ applySignalPage(signals, snapshots) {
99213
+ const snapshotsByKey = new Map(snapshots.map((snapshot) => [
99214
+ snapshotKey(snapshot.taskId, snapshot.agentConfig.role),
99215
+ snapshot
99216
+ ]));
99217
+ for (const signal of signals) {
99218
+ const key = snapshotKey(signal.taskId, signal.targetRole);
99219
+ const snapshot = snapshotsByKey.get(key);
99220
+ if (snapshot) {
99221
+ this.snapshots.set(key, snapshot);
99222
+ } else {
99223
+ this.snapshots.delete(key);
99224
+ }
99225
+ }
99226
+ }
99227
+ listForRole(chatroomId, role) {
99228
+ const roleLower = role.toLowerCase();
99229
+ return [...this.snapshots.values()].filter((snapshot) => snapshot.chatroomId === chatroomId && snapshot.agentConfig.role.toLowerCase() === roleLower);
99230
+ }
99231
+ listAll() {
99232
+ return [...this.snapshots.values()];
99233
+ }
99234
+ upsert(snapshots) {
99235
+ for (const snapshot of snapshots) {
99236
+ this.snapshots.set(snapshotKey(snapshot.taskId, snapshot.agentConfig.role), snapshot);
99237
+ }
99238
+ }
99239
+ }
99240
+
99241
+ // src/daemon/entry/native-delivery/native-delivery-session-registry.ts
99242
+ function registerNativeDeliverySession(ctx) {
99243
+ registered = {
99244
+ ...ctx,
99245
+ taskSnapshotState: ctx.taskSnapshotState ?? new MachineTaskSnapshotState,
99246
+ agentOperationalReadModel: ctx.agentOperationalReadModel ?? new AgentOperationalReadModel
99247
+ };
99248
+ }
99249
+ function unregisterNativeDeliverySession() {
99250
+ registered = null;
99251
+ }
99252
+ function getNativeDeliverySession() {
99253
+ return registered;
99254
+ }
99255
+ var registered = null;
99256
+ var init_native_delivery_session_registry = () => {};
99257
+
98892
99258
  // src/daemon/domain/entities/assigned-task.ts
98893
99259
  function isDeliverableTaskStatus(status3) {
98894
99260
  return status3 === "pending" || status3 === "acknowledged";
98895
99261
  }
98896
- function isAgentDesiredRunning(desiredState) {
98897
- return desiredState === "running";
98898
- }
98899
99262
  var init_assigned_task = () => {};
98900
99263
 
98901
99264
  // src/daemon/domain/usecase/check-agent-slot.ts
@@ -98925,8 +99288,9 @@ function explainAgentReadyForNativeDeliveryBlock(task, slot) {
98925
99288
  if (!isNativeHarness(agentConfig.agentHarness)) {
98926
99289
  return `not_native_harness (harness=${agentConfig.agentHarness})`;
98927
99290
  }
98928
- if (!isAgentDesiredRunning(agentConfig.desiredState)) {
98929
- return `desired_state_not_running (desiredState=${agentConfig.desiredState})`;
99291
+ const operational = getNativeDeliverySession()?.agentOperationalReadModel?.get(task.chatroomId, agentConfig.role);
99292
+ if (!isOperationalDesiredRunning(operational)) {
99293
+ return `operational_state_not_running (state=${operational?.operationalState ?? "missing"})`;
98930
99294
  }
98931
99295
  if (!slot) {
98932
99296
  return agentConfig.spawnedAgentPid == null ? "spawned_pid_missing" : `slot_missing (expectedPid=${agentConfig.spawnedAgentPid})`;
@@ -98954,6 +99318,7 @@ function isDeliverableNativeTaskStatus(status3) {
98954
99318
  }
98955
99319
  var init_native_ready_invariant = __esm(() => {
98956
99320
  init_types();
99321
+ init_native_delivery_session_registry();
98957
99322
  init_assigned_task();
98958
99323
  init_check_agent_slot();
98959
99324
  });
@@ -99466,20 +99831,20 @@ function mapAssignedTaskSnapshotList(rows) {
99466
99831
  }
99467
99832
 
99468
99833
  // src/daemon/entry/restart-orchestrator-in-flight.ts
99469
- function roleKey(chatroomId, role) {
99834
+ function roleKey2(chatroomId, role) {
99470
99835
  return `${chatroomId}:${role.toLowerCase()}`;
99471
99836
  }
99472
99837
  function markRestartOrchestratorInFlight(chatroomId, role, correlationId) {
99473
- inFlight.set(roleKey(chatroomId, role), correlationId);
99838
+ inFlight.set(roleKey2(chatroomId, role), correlationId);
99474
99839
  }
99475
99840
  function clearRestartOrchestratorInFlight(chatroomId, role, correlationId) {
99476
- const key = roleKey(chatroomId, role);
99841
+ const key = roleKey2(chatroomId, role);
99477
99842
  if (correlationId !== undefined && inFlight.get(key) !== correlationId)
99478
99843
  return;
99479
99844
  inFlight.delete(key);
99480
99845
  }
99481
99846
  function isRestartOrchestratorInFlight(chatroomId, role) {
99482
- return inFlight.has(roleKey(chatroomId, role));
99847
+ return inFlight.has(roleKey2(chatroomId, role));
99483
99848
  }
99484
99849
  function filterSnapshotsExcludingRestartInFlight(snapshots) {
99485
99850
  return snapshots.filter((row) => !isRestartOrchestratorInFlight(row.chatroomId, row.agentConfig.role));
@@ -99490,7 +99855,7 @@ var init_restart_orchestrator_in_flight = __esm(() => {
99490
99855
  });
99491
99856
 
99492
99857
  // src/daemon/entry/role-delivery-state.ts
99493
- function roleKey2(chatroomId, role) {
99858
+ function roleKey3(chatroomId, role) {
99494
99859
  return `${chatroomId}:${role.toLowerCase()}`;
99495
99860
  }
99496
99861
 
@@ -99499,10 +99864,10 @@ class RoleDeliveryState {
99499
99864
  inFlight = new Set;
99500
99865
  nativeNudgeFailures = new Map;
99501
99866
  getGeneration(chatroomId, role) {
99502
- return this.generation.get(roleKey2(chatroomId, role)) ?? 0;
99867
+ return this.generation.get(roleKey3(chatroomId, role)) ?? 0;
99503
99868
  }
99504
99869
  resetDeliveryState(chatroomId, role) {
99505
- const key = roleKey2(chatroomId, role);
99870
+ const key = roleKey3(chatroomId, role);
99506
99871
  const next4 = (this.generation.get(key) ?? 0) + 1;
99507
99872
  this.generation.set(key, next4);
99508
99873
  this.inFlight.delete(key);
@@ -99510,26 +99875,26 @@ class RoleDeliveryState {
99510
99875
  return next4;
99511
99876
  }
99512
99877
  tryAcquireDelivery(chatroomId, role) {
99513
- const key = roleKey2(chatroomId, role);
99878
+ const key = roleKey3(chatroomId, role);
99514
99879
  if (this.inFlight.has(key))
99515
99880
  return false;
99516
99881
  this.inFlight.add(key);
99517
99882
  return true;
99518
99883
  }
99519
99884
  releaseDelivery(chatroomId, role) {
99520
- this.inFlight.delete(roleKey2(chatroomId, role));
99885
+ this.inFlight.delete(roleKey3(chatroomId, role));
99521
99886
  }
99522
99887
  recordNativeNudgeFailure(chatroomId, role) {
99523
- const key = roleKey2(chatroomId, role);
99888
+ const key = roleKey3(chatroomId, role);
99524
99889
  const count3 = (this.nativeNudgeFailures.get(key) ?? 0) + 1;
99525
99890
  this.nativeNudgeFailures.set(key, count3);
99526
99891
  return count3;
99527
99892
  }
99528
99893
  clearNativeNudgeFailures(chatroomId, role) {
99529
- this.nativeNudgeFailures.delete(roleKey2(chatroomId, role));
99894
+ this.nativeNudgeFailures.delete(roleKey3(chatroomId, role));
99530
99895
  }
99531
99896
  getNativeNudgeFailures(chatroomId, role) {
99532
- return this.nativeNudgeFailures.get(roleKey2(chatroomId, role)) ?? 0;
99897
+ return this.nativeNudgeFailures.get(roleKey3(chatroomId, role)) ?? 0;
99533
99898
  }
99534
99899
  }
99535
99900
  function getRoleDeliveryState() {
@@ -99916,8 +100281,6 @@ function createStartAgentDeps(agentMgr, session2) {
99916
100281
  return {
99917
100282
  agentProcessManager: {
99918
100283
  ensureRunning: async (args2) => {
99919
- const taskSnapshotState = getNativeDeliverySession()?.taskSnapshotState;
99920
- taskSnapshotState?.setDesiredState(args2.chatroomId, args2.role, "running");
99921
100284
  const result = await exports_Effect.runPromise(agentMgr.ensureRunning({
99922
100285
  chatroomId: args2.chatroomId,
99923
100286
  role: args2.role,
@@ -99927,9 +100290,7 @@ function createStartAgentDeps(agentMgr, session2) {
99927
100290
  reason: args2.reason,
99928
100291
  wantResume: args2.wantResume
99929
100292
  }));
99930
- if (!result.success) {
99931
- taskSnapshotState?.setDesiredState(args2.chatroomId, args2.role, "stopped");
99932
- }
100293
+ if (!result.success) {}
99933
100294
  return result;
99934
100295
  }
99935
100296
  },
@@ -100052,7 +100413,6 @@ function createRecoverAgentStateDeps(agentMgr, session2) {
100052
100413
  var init_agent_control_bridge = __esm(() => {
100053
100414
  init_esm();
100054
100415
  init_api3();
100055
- init_native_delivery_session_registry();
100056
100416
  init_restart_orchestrator();
100057
100417
  });
100058
100418
 
@@ -100188,9 +100548,9 @@ var init_daemon_process_scan = __esm(() => {
100188
100548
 
100189
100549
  // src/commands/machine/pid.ts
100190
100550
  import { createHash as createHash3 } from "node:crypto";
100191
- import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync5 } from "node:fs";
100192
- import { homedir as homedir6 } from "node:os";
100193
- import { join as join19 } from "node:path";
100551
+ import { existsSync as existsSync8, readFileSync as readFileSync10, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2, mkdirSync as mkdirSync6 } from "node:fs";
100552
+ import { homedir as homedir7 } from "node:os";
100553
+ import { join as join20 } from "node:path";
100194
100554
  function getUrlHash2() {
100195
100555
  const url3 = getConvexUrl();
100196
100556
  return createHash3("sha256").update(url3).digest("hex").substring(0, 8);
@@ -100200,11 +100560,11 @@ function getPidFileName() {
100200
100560
  }
100201
100561
  function ensureChatroomDir3() {
100202
100562
  if (!existsSync8(CHATROOM_DIR4)) {
100203
- mkdirSync5(CHATROOM_DIR4, { recursive: true, mode: 448 });
100563
+ mkdirSync6(CHATROOM_DIR4, { recursive: true, mode: 448 });
100204
100564
  }
100205
100565
  }
100206
100566
  function getPidFilePath() {
100207
- return join19(CHATROOM_DIR4, getPidFileName());
100567
+ return join20(CHATROOM_DIR4, getPidFileName());
100208
100568
  }
100209
100569
  function isProcessRunning(pid) {
100210
100570
  try {
@@ -100350,7 +100710,7 @@ var CHATROOM_DIR4, LOCK_RETRY_INTERVAL_MS = 500, LOCK_RETRY_MAX_WAIT_MS = 15000,
100350
100710
  var init_pid = __esm(() => {
100351
100711
  init_daemon_process_scan();
100352
100712
  init_client2();
100353
- CHATROOM_DIR4 = join19(homedir6(), ".chatroom");
100713
+ CHATROOM_DIR4 = join20(homedir7(), ".chatroom");
100354
100714
  });
100355
100715
 
100356
100716
  // src/infrastructure/machine/crash-loop-tracker.ts
@@ -102096,9 +102456,8 @@ class AgentProcessManager {
102096
102456
  console.log(` ⚠️ ${failureLog}: ${err.message}`);
102097
102457
  this.queueExitRetry({ role, args: exitArgs });
102098
102458
  });
102099
- this.deps.backend.mutation(api.daemon.agentEvents.agentExited, exitArgs).catch((err) => {
102100
- console.log(` ⚠️ Failed to apply agent exit state: ${err.message}`);
102101
- });
102459
+ const emittedAt = this.deps.clock.now();
102460
+ this.deps.lifecycleOutbox.enqueue(buildExitedLifecycleFact(exitArgs, emittedAt)).catch((err) => console.log(` ⚠️ Failed to enqueue agent exited lifecycle fact: ${err.message}`));
102102
102461
  }
102103
102462
  async clearAgentPidQuietly(chatroomId, role) {
102104
102463
  try {
@@ -102502,28 +102861,23 @@ class AgentProcessManager {
102502
102861
  }).catch((err) => {
102503
102862
  console.log(` ⚠️ Failed to record agent.started event: ${err.message}`);
102504
102863
  });
102505
- this.deps.backend.mutation(api.daemon.agentEvents.agentStarted, {
102506
- sessionId: this.deps.sessionId,
102507
- machineId: this.deps.machineId,
102864
+ const emittedAt = this.deps.clock.now();
102865
+ this.deps.lifecycleOutbox.enqueue({
102866
+ kind: "spawned",
102508
102867
  chatroomId: opts.chatroomId,
102509
102868
  role: opts.role,
102510
102869
  pid,
102511
102870
  model: opts.model,
102512
102871
  reason: opts.reason,
102513
- ...spawnResult.harnessSessionId ? { harnessSessionId: spawnResult.harnessSessionId } : {}
102514
- }).catch((err) => {
102515
- console.log(` ⚠️ Failed to apply agent.started state: ${err.message}`);
102516
- });
102517
- this.deps.backend.mutation(api.machines.updateSpawnedAgent, {
102518
- sessionId: this.deps.sessionId,
102519
- machineId: this.deps.machineId,
102520
- chatroomId: opts.chatroomId,
102521
- role: opts.role,
102522
- pid,
102523
- model: opts.model
102524
- }).catch((err) => {
102525
- console.log(` ⚠️ Failed to update PID in backend: ${err.message}`);
102526
- });
102872
+ ...spawnResult.harnessSessionId ? { harnessSessionId: spawnResult.harnessSessionId } : {},
102873
+ revisionKey: buildAgentLifecycleRevisionKey("spawned", {
102874
+ chatroomId: opts.chatroomId,
102875
+ role: opts.role,
102876
+ pid,
102877
+ emittedAt
102878
+ }),
102879
+ emittedAt
102880
+ }).catch((err) => console.log(` ⚠️ Failed to enqueue agent spawned lifecycle fact: ${err.message}`));
102527
102881
  }
102528
102882
  registerSpawnCallbacks(slot, opts, spawnResult, pid) {
102529
102883
  if (spawnResult.onLogLine) {
@@ -102878,6 +103232,11 @@ function assembleDaemonSessionInit(args2) {
102878
103232
  } = args2;
102879
103233
  deps.backend.mutation = (endpoint, args3) => client4.mutation(endpoint, args3);
102880
103234
  deps.backend.query = (endpoint, args3) => client4.query(endpoint, args3);
103235
+ const agentLifecycleOutbox = createAgentLifecycleOutboxForSession({
103236
+ sessionId: typedSessionId,
103237
+ machineId,
103238
+ backend: deps.backend
103239
+ });
102881
103240
  deps.agentProcessManager = new AgentProcessManager({
102882
103241
  logEvent: activeLogEvent ?? (async () => {
102883
103242
  return;
@@ -102893,7 +103252,8 @@ function assembleDaemonSessionInit(args2) {
102893
103252
  persistence: deps.machine,
102894
103253
  spawning: deps.spawning,
102895
103254
  crashLoop: new CrashLoopTracker,
102896
- convexUrl
103255
+ convexUrl,
103256
+ lifecycleOutbox: { enqueue: (fact) => enqueueAgentLifecycleFact(agentLifecycleOutbox, machineId, fact) }
102897
103257
  });
102898
103258
  return {
102899
103259
  client: client4,
@@ -102906,6 +103266,7 @@ function assembleDaemonSessionInit(args2) {
102906
103266
  machine: deps.machine,
102907
103267
  spawning: deps.spawning,
102908
103268
  agentProcessManager: deps.agentProcessManager,
103269
+ agentLifecycleOutbox,
102909
103270
  events: new DaemonEventBus,
102910
103271
  agentServices,
102911
103272
  lastPushedGitState: new Map,
@@ -103138,6 +103499,8 @@ var init_init_daemon = __esm(() => {
103138
103499
  init_daemon_startup_log();
103139
103500
  init_orphan_tracker();
103140
103501
  init_output_store();
103502
+ init_agent_lifecycle_outbox_runtime();
103503
+ init_agent_lifecycle_outbox_runtime();
103141
103504
  init_state_recovery();
103142
103505
  init_pid();
103143
103506
  init_storage();
@@ -105768,6 +106131,14 @@ var init_workspace_cache = __esm(() => {
105768
106131
 
105769
106132
  // src/daemon/infrastructure/git/run-command.ts
105770
106133
  import { spawn as spawn4 } from "node:child_process";
106134
+ function enqueueGit(cwd, task) {
106135
+ const tail = gitQueues.get(cwd) ?? Promise.resolve();
106136
+ const run3 = tail.then(task, task);
106137
+ gitQueues.set(cwd, run3.catch(() => {
106138
+ return;
106139
+ }));
106140
+ return run3;
106141
+ }
105771
106142
  function runCommandSpawn(command, args2, cwd, options) {
105772
106143
  const successExitCodes = options?.successExitCodes ?? [0];
105773
106144
  return new Promise((resolve4) => {
@@ -105813,7 +106184,7 @@ function runCommandSpawn(command, args2, cwd, options) {
105813
106184
  });
105814
106185
  }
105815
106186
  function runGit(args2, cwd, options) {
105816
- return runCommandSpawn("git", args2, cwd, options);
106187
+ return enqueueGit(cwd, () => runCommandSpawn("git", options?.readOnly ? ["--no-optional-locks", ...args2] : args2, cwd, options));
105817
106188
  }
105818
106189
  function runGh(args2, cwd, options) {
105819
106190
  return runCommandSpawn("gh", args2, cwd, {
@@ -105821,7 +106192,7 @@ function runGh(args2, cwd, options) {
105821
106192
  env: { ...process.env, NO_COLOR: "1" }
105822
106193
  });
105823
106194
  }
105824
- var DEFAULT_GIT_ENV;
106195
+ var DEFAULT_GIT_ENV, gitQueues;
105825
106196
  var init_run_command = __esm(() => {
105826
106197
  DEFAULT_GIT_ENV = {
105827
106198
  ...process.env,
@@ -105829,6 +106200,7 @@ var init_run_command = __esm(() => {
105829
106200
  GIT_PAGER: "cat",
105830
106201
  NO_COLOR: "1"
105831
106202
  };
106203
+ gitQueues = new Map;
105832
106204
  });
105833
106205
 
105834
106206
  // src/daemon/infrastructure/git/types.ts
@@ -105838,6 +106210,9 @@ function makeGitStateKey(machineId, workingDir) {
105838
106210
  var FULL_DIFF_MAX_BYTES = 500000, COMMITS_PER_PAGE = 20;
105839
106211
 
105840
106212
  // src/daemon/infrastructure/git/git-reader.ts
106213
+ function runGit2(args2, cwd, options) {
106214
+ return runGit(cwd ? args2 : args2, cwd, { ...options, readOnly: true });
106215
+ }
105841
106216
  function repoArgs(repoSlug) {
105842
106217
  return repoSlug ? ["--repo", repoSlug] : [];
105843
106218
  }
@@ -105854,14 +106229,14 @@ function isEmptyRepo(stderr) {
105854
106229
  return stderr.includes("does not have any commits yet") || stderr.includes("no commits yet") || stderr.includes("ambiguous argument 'HEAD'") || stderr.includes("unknown revision or path");
105855
106230
  }
105856
106231
  async function listUntrackedFiles(workingDir) {
105857
- const result = await runGit(["ls-files", "--others", "--exclude-standard"], workingDir);
106232
+ const result = await runGit2(["ls-files", "--others", "--exclude-standard"], workingDir);
105858
106233
  if ("error" in result)
105859
106234
  return [];
105860
106235
  return result.stdout.split(`
105861
106236
  `).map((line) => line.trim()).filter(Boolean);
105862
106237
  }
105863
106238
  async function getUntrackedFileDiff(workingDir, filePath) {
105864
- const result = await runGit(["diff", "--no-index", "--", NULL_DEVICE, filePath], workingDir, {
106239
+ const result = await runGit2(["diff", "--no-index", "--", NULL_DEVICE, filePath], workingDir, {
105865
106240
  successExitCodes: [0, 1]
105866
106241
  });
105867
106242
  if ("error" in result)
@@ -105922,7 +106297,7 @@ function truncateDiffContent(raw) {
105922
106297
  return { status: "available", content: raw, truncated: false };
105923
106298
  }
105924
106299
  async function runTrackedHeadDiff(workingDir, args2, options) {
105925
- const result = await runGit(args2, workingDir, options);
106300
+ const result = await runGit2(args2, workingDir, options);
105926
106301
  if ("error" in result) {
105927
106302
  const errMsg = result.error.message;
105928
106303
  if (isEmptyRepo(errMsg))
@@ -105951,13 +106326,13 @@ function classifyError(errMessage) {
105951
106326
  return { status: "error", message: errMessage.trim() };
105952
106327
  }
105953
106328
  async function isGitRepo(workingDir) {
105954
- const result = await runGit(["rev-parse", "--git-dir"], workingDir);
106329
+ const result = await runGit2(["rev-parse", "--git-dir"], workingDir);
105955
106330
  if ("error" in result)
105956
106331
  return false;
105957
106332
  return result.stdout.trim().length > 0;
105958
106333
  }
105959
106334
  async function getBranch(workingDir) {
105960
- const result = await runGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
106335
+ const result = await runGit2(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
105961
106336
  if ("error" in result) {
105962
106337
  const errMsg = result.error.message;
105963
106338
  if (errMsg.includes("unknown revision") || errMsg.includes("No such file or directory") || errMsg.includes("does not have any commits")) {
@@ -105972,7 +106347,7 @@ async function getBranch(workingDir) {
105972
106347
  return { status: "available", branch };
105973
106348
  }
105974
106349
  async function isDirty(workingDir) {
105975
- const result = await runGit(["status", "--porcelain"], workingDir);
106350
+ const result = await runGit2(["status", "--porcelain"], workingDir);
105976
106351
  if ("error" in result)
105977
106352
  return false;
105978
106353
  return result.stdout.trim().length > 0;
@@ -106042,7 +106417,7 @@ async function getRecentCommits(workingDir, count3 = 20, skip = 0) {
106042
106417
  if (skip > 0)
106043
106418
  logArgs.push(`--skip=${skip}`);
106044
106419
  logArgs.push(`--format=${format4}`);
106045
- const result = await runGit(logArgs, workingDir);
106420
+ const result = await runGit2(logArgs, workingDir);
106046
106421
  if ("error" in result) {
106047
106422
  return [];
106048
106423
  }
@@ -106064,7 +106439,7 @@ async function getRecentCommits(workingDir, count3 = 20, skip = 0) {
106064
106439
  return commits;
106065
106440
  }
106066
106441
  async function getCommitDetail(workingDir, sha) {
106067
- const result = await runGit(["show", sha, "--format=", "--stat", "-p"], workingDir);
106442
+ const result = await runGit2(["show", sha, "--format=", "--stat", "-p"], workingDir);
106068
106443
  if ("error" in result) {
106069
106444
  const errMsg = result.error.message;
106070
106445
  const classified = classifyError(errMsg);
@@ -106085,7 +106460,7 @@ async function getCommitDetail(workingDir, sha) {
106085
106460
  }
106086
106461
  async function getCommitMetadata(workingDir, sha) {
106087
106462
  const format4 = "%s%x1f%b%x1f%an%x1f%aI";
106088
- const result = await runGit(["log", "-1", `--format=${format4}`, sha], workingDir);
106463
+ const result = await runGit2(["log", "-1", `--format=${format4}`, sha], workingDir);
106089
106464
  if ("error" in result)
106090
106465
  return null;
106091
106466
  const output = result.stdout.trim();
@@ -106111,7 +106486,7 @@ function parseRepoSlug(remoteUrl) {
106111
106486
  return null;
106112
106487
  }
106113
106488
  async function getOriginRepoSlug(cwd) {
106114
- const result = await runGit(["remote", "get-url", "origin"], cwd);
106489
+ const result = await runGit2(["remote", "get-url", "origin"], cwd);
106115
106490
  if ("error" in result)
106116
106491
  return null;
106117
106492
  const url3 = result.stdout.trim();
@@ -106248,7 +106623,7 @@ async function getPRCommits(cwd, prNumber) {
106248
106623
  }
106249
106624
  }
106250
106625
  async function getRemotes(cwd) {
106251
- const result = await runGit(["remote", "-v"], cwd);
106626
+ const result = await runGit2(["remote", "-v"], cwd);
106252
106627
  if ("error" in result)
106253
106628
  return [];
106254
106629
  if (!result.stdout.trim())
@@ -106272,14 +106647,14 @@ async function getRemotes(cwd) {
106272
106647
  return remotes;
106273
106648
  }
106274
106649
  async function getCommitsAhead(workingDir) {
106275
- const result = await runGit(["rev-list", "--count", "@{upstream}..HEAD"], workingDir);
106650
+ const result = await runGit2(["rev-list", "--count", "@{upstream}..HEAD"], workingDir);
106276
106651
  if ("error" in result)
106277
106652
  return 0;
106278
106653
  const count3 = parseInt(result.stdout.trim(), 10);
106279
106654
  return Number.isNaN(count3) ? 0 : count3;
106280
106655
  }
106281
106656
  async function getCommitsBehind(workingDir) {
106282
- const result = await runGit(["rev-list", "--count", "HEAD..@{upstream}"], workingDir);
106657
+ const result = await runGit2(["rev-list", "--count", "HEAD..@{upstream}"], workingDir);
106283
106658
  if ("error" in result)
106284
106659
  return 0;
106285
106660
  const count3 = parseInt(result.stdout.trim(), 10);
@@ -106373,16 +106748,12 @@ class GitStatePipeline {
106373
106748
  const results = new Map(preCollected);
106374
106749
  if (this.fields.length === 0)
106375
106750
  return results;
106376
- const entries2 = await Promise.all(this.fields.filter((f) => !results.has(f.key)).map(async (field) => {
106751
+ for (const field of this.fields.filter((f) => !results.has(f.key))) {
106377
106752
  try {
106378
- const raw = await field.collect(workingDir);
106379
- return { key: field.key, raw };
106753
+ results.set(field.key, await field.collect(workingDir));
106380
106754
  } catch {
106381
- return { key: field.key, raw: field.defaultValue };
106755
+ results.set(field.key, field.defaultValue);
106382
106756
  }
106383
- }));
106384
- for (const { key, raw } of entries2) {
106385
- results.set(key, raw);
106386
106757
  }
106387
106758
  return results;
106388
106759
  }
@@ -107547,16 +107918,16 @@ var init_jsonc = __esm(() => {
107547
107918
 
107548
107919
  // src/infrastructure/services/workspace/workspace-resolver.ts
107549
107920
  import { readFile as readFile7, readdir, stat as stat3 } from "node:fs/promises";
107550
- import { join as join20, basename, resolve as resolve4 } from "node:path";
107921
+ import { join as join21, basename, resolve as resolve4 } from "node:path";
107551
107922
  async function resolveGlobPatternStar(rootDir, cleaned) {
107552
- const parentDir = join20(rootDir, cleaned.slice(0, -2));
107923
+ const parentDir = join21(rootDir, cleaned.slice(0, -2));
107553
107924
  try {
107554
107925
  const entries2 = await readdir(parentDir, { withFileTypes: true });
107555
107926
  const dirs = [];
107556
107927
  for (const entry of entries2) {
107557
107928
  if (!entry.isDirectory())
107558
107929
  continue;
107559
- const dirPath = join20(parentDir, entry.name);
107930
+ const dirPath = join21(parentDir, entry.name);
107560
107931
  if (resolve4(dirPath).startsWith(resolve4(rootDir))) {
107561
107932
  dirs.push(dirPath);
107562
107933
  }
@@ -107567,7 +107938,7 @@ async function resolveGlobPatternStar(rootDir, cleaned) {
107567
107938
  }
107568
107939
  }
107569
107940
  async function resolveLiteralPath(rootDir, cleaned) {
107570
- const dir = join20(rootDir, cleaned);
107941
+ const dir = join21(rootDir, cleaned);
107571
107942
  try {
107572
107943
  if (!resolve4(dir).startsWith(resolve4(rootDir)))
107573
107944
  return [];
@@ -107587,7 +107958,7 @@ async function resolveGlobPattern(rootDir, pattern) {
107587
107958
  }
107588
107959
  async function readPackageJson(dir) {
107589
107960
  try {
107590
- const content = await readFile7(join20(dir, "package.json"), "utf-8");
107961
+ const content = await readFile7(join21(dir, "package.json"), "utf-8");
107591
107962
  const pkg = JSON.parse(content);
107592
107963
  return {
107593
107964
  name: pkg.name || basename(dir),
@@ -107599,7 +107970,7 @@ async function readPackageJson(dir) {
107599
107970
  }
107600
107971
  async function readPnpmWorkspacePatterns(rootDir) {
107601
107972
  try {
107602
- const content = await readFile7(join20(rootDir, "pnpm-workspace.yaml"), "utf-8");
107973
+ const content = await readFile7(join21(rootDir, "pnpm-workspace.yaml"), "utf-8");
107603
107974
  const patterns = [];
107604
107975
  let inPackages = false;
107605
107976
  for (const line of content.split(`
@@ -107626,7 +107997,7 @@ async function readPnpmWorkspacePatterns(rootDir) {
107626
107997
  }
107627
107998
  async function readPackageJsonWorkspacePatterns(rootDir) {
107628
107999
  try {
107629
- const content = await readFile7(join20(rootDir, "package.json"), "utf-8");
108000
+ const content = await readFile7(join21(rootDir, "package.json"), "utf-8");
107630
108001
  const pkg = JSON.parse(content);
107631
108002
  if (!pkg.workspaces)
107632
108003
  return [];
@@ -107707,11 +108078,11 @@ var MAX_NAME_LENGTH = 256;
107707
108078
 
107708
108079
  // src/infrastructure/services/workspace/command-discovery.ts
107709
108080
  import { access as access2, readFile as readFile8 } from "node:fs/promises";
107710
- import { join as join21, relative, basename as basename2 } from "node:path";
108081
+ import { join as join22, relative, basename as basename2 } from "node:path";
107711
108082
  async function detectPackageManager(workingDir) {
107712
108083
  for (const { file: file2, manager } of LOCKFILE_MAP) {
107713
108084
  try {
107714
- await access2(join21(workingDir, file2));
108085
+ await access2(join22(workingDir, file2));
107715
108086
  return manager;
107716
108087
  } catch {}
107717
108088
  }
@@ -107780,7 +108151,7 @@ function collectRootScriptCommands(scripts, pm, scriptPrefix, rootSw) {
107780
108151
  }
107781
108152
  async function readRootPackageJson(workingDir, pm, scriptPrefix) {
107782
108153
  let rootPackageName = basename2(workingDir);
107783
- const pkg = await readJsonFile(join21(workingDir, "package.json"), "root package.json");
108154
+ const pkg = await readJsonFile(join22(workingDir, "package.json"), "root package.json");
107784
108155
  if (!pkg)
107785
108156
  return { commands: [], rootPackageName };
107786
108157
  if (pkg.name)
@@ -107796,7 +108167,7 @@ async function readRootPackageJson(workingDir, pm, scriptPrefix) {
107796
108167
  }
107797
108168
  async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
107798
108169
  const turboTaskNames = [];
107799
- const turbo = await readJsonFile(join21(workingDir, "turbo.json"), "turbo.json");
108170
+ const turbo = await readJsonFile(join22(workingDir, "turbo.json"), "turbo.json");
107800
108171
  if (!turbo?.tasks || typeof turbo.tasks !== "object")
107801
108172
  return turboTaskNames;
107802
108173
  for (const taskName of Object.keys(turbo.tasks)) {
@@ -107809,7 +108180,7 @@ async function readTurboJson(workingDir, _turboPrefix, _rootSubWorkspace) {
107809
108180
  async function readDenoJson(workingDir) {
107810
108181
  const commands = [];
107811
108182
  for (const fileName of ["deno.json", "deno.jsonc"]) {
107812
- const deno = await readJsonFile(join21(workingDir, fileName), fileName);
108183
+ const deno = await readJsonFile(join22(workingDir, fileName), fileName);
107813
108184
  if (!deno?.tasks || typeof deno.tasks !== "object")
107814
108185
  continue;
107815
108186
  const pkgName = deno.name ?? basename2(workingDir);
@@ -107832,7 +108203,7 @@ async function readMakefile(workingDir) {
107832
108203
  const commands = [];
107833
108204
  for (const fileName of ["Makefile", "makefile"]) {
107834
108205
  try {
107835
- const content = await readFile8(join21(workingDir, fileName), "utf-8");
108206
+ const content = await readFile8(join22(workingDir, fileName), "utf-8");
107836
108207
  const targets = parseMakefileTargets(content);
107837
108208
  if (targets.length === 0)
107838
108209
  continue;
@@ -110038,7 +110409,7 @@ var init_assert_registered_working_dir = __esm(() => {
110038
110409
 
110039
110410
  // src/infrastructure/services/workspace/workspace-path-security.ts
110040
110411
  import { realpath } from "node:fs/promises";
110041
- import { dirname as dirname9, isAbsolute, relative as relative2, resolve as resolve5, sep } from "node:path";
110412
+ import { dirname as dirname10, isAbsolute, relative as relative2, resolve as resolve5, sep } from "node:path";
110042
110413
  import { gunzipSync } from "node:zlib";
110043
110414
  function validateRelativePathSegments(filePath) {
110044
110415
  if (filePath.includes("\x00"))
@@ -110066,7 +110437,7 @@ function assertContained(workspaceRoot, targetPath) {
110066
110437
  return { ok: true };
110067
110438
  }
110068
110439
  async function resolveNonExistentTarget(workspaceRoot, candidate) {
110069
- let current = dirname9(candidate);
110440
+ let current = dirname10(candidate);
110070
110441
  for (;; ) {
110071
110442
  try {
110072
110443
  const ancestorReal = await realpath(current);
@@ -110084,7 +110455,7 @@ async function resolveNonExistentTarget(workspaceRoot, candidate) {
110084
110455
  if (code2 !== "ENOENT") {
110085
110456
  return { ok: false, error: "Invalid file path" };
110086
110457
  }
110087
- const parent = dirname9(current);
110458
+ const parent = dirname10(current);
110088
110459
  if (parent === current) {
110089
110460
  return { ok: false, error: "Path escapes workspace" };
110090
110461
  }
@@ -110895,16 +111266,16 @@ var require_ignore = __commonJS((exports, module) => {
110895
111266
  // src/infrastructure/services/workspace/workspace-ignore.ts
110896
111267
  import { promises as fsPromises } from "node:fs";
110897
111268
  import { readFile as readFile10 } from "node:fs/promises";
110898
- import { homedir as homedir7 } from "node:os";
111269
+ import { homedir as homedir8 } from "node:os";
110899
111270
  import path3 from "node:path";
110900
111271
  function normalizeRelativePath(relativePath) {
110901
111272
  return relativePath.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+$/, "");
110902
111273
  }
110903
111274
  function expandUserPath(filePath) {
110904
111275
  if (filePath === "~")
110905
- return homedir7();
111276
+ return homedir8();
110906
111277
  if (filePath.startsWith("~/"))
110907
- return path3.join(homedir7(), filePath.slice(2));
111278
+ return path3.join(homedir8(), filePath.slice(2));
110908
111279
  return filePath;
110909
111280
  }
110910
111281
  function readExcludesFileFromGitconfig(content) {
@@ -110935,11 +111306,11 @@ async function readGitconfigExcludesFilePath(configPath) {
110935
111306
  return path3.isAbsolute(raw) ? expandUserPath(raw) : path3.resolve(path3.dirname(configPath), raw);
110936
111307
  }
110937
111308
  async function resolveGitExtraExcludeFilePaths(rootDir) {
110938
- const globalConfig2 = process.env.GIT_CONFIG_GLOBAL?.length ? process.env.GIT_CONFIG_GLOBAL : path3.join(homedir7(), ".gitconfig");
111309
+ const globalConfig2 = process.env.GIT_CONFIG_GLOBAL?.length ? process.env.GIT_CONFIG_GLOBAL : path3.join(homedir8(), ".gitconfig");
110939
111310
  const localConfig = path3.join(rootDir, ".git", "config");
110940
111311
  let excludesFile = await readGitconfigExcludesFilePath(localConfig) ?? await readGitconfigExcludesFilePath(globalConfig2);
110941
111312
  if (!excludesFile) {
110942
- excludesFile = path3.join(process.env.XDG_CONFIG_HOME?.length ? process.env.XDG_CONFIG_HOME : path3.join(homedir7(), ".config"), "git", "ignore");
111313
+ excludesFile = path3.join(process.env.XDG_CONFIG_HOME?.length ? process.env.XDG_CONFIG_HOME : path3.join(homedir8(), ".config"), "git", "ignore");
110943
111314
  }
110944
111315
  const candidates = [excludesFile, path3.join(rootDir, ".git", "info", "exclude")];
110945
111316
  const existing = [];
@@ -111391,7 +111762,8 @@ function porcelainUntrackedDeletedEvents(args2) {
111391
111762
  }
111392
111763
  async function readGitHead(workTree) {
111393
111764
  const result = await runGit(["rev-parse", "HEAD"], workTree, {
111394
- timeout: GIT_POLL_TIMEOUT_MS
111765
+ timeout: GIT_POLL_TIMEOUT_MS,
111766
+ readOnly: true
111395
111767
  });
111396
111768
  if ("error" in result) {
111397
111769
  if (isEmptyRepoHeadError(result.error))
@@ -111415,7 +111787,10 @@ async function readGitPorcelainStatus(node) {
111415
111787
  const args2 = ["status", "--porcelain=v1", "-z", "-uall", "--"];
111416
111788
  if (node.pathspec.length > 0)
111417
111789
  args2.push(...node.pathspec);
111418
- const result = await runGit(args2, node.workTree, { timeout: GIT_POLL_TIMEOUT_MS });
111790
+ const result = await runGit(args2, node.workTree, {
111791
+ timeout: GIT_POLL_TIMEOUT_MS,
111792
+ readOnly: true
111793
+ });
111419
111794
  if ("error" in result) {
111420
111795
  throw new GitWorkspaceCommandError({
111421
111796
  operation: "readGitPorcelainStatus",
@@ -111553,35 +111928,31 @@ function createGitWorkspaceChangeSource(options) {
111553
111928
  let needsReconcile = false;
111554
111929
  let tickHadFailure = false;
111555
111930
  let maxNodeFailures = 0;
111556
- const results = await Promise.allSettled(nodes.map(async (node) => {
111557
- const prev = state.get(node.workTree) ?? {
111558
- prevEntries: [],
111559
- prevHead: { head: null },
111560
- baselineEstablished: false
111561
- };
111562
- const result = await pollGitWorkspaceNode({
111563
- options,
111564
- node,
111565
- prev
111566
- });
111567
- allEvents.push(...result.events);
111568
- if (result.needsReconcile)
111569
- needsReconcile = true;
111570
- state.set(node.workTree, result.nextState);
111571
- nodeFailureCounts.set(node.workTree, 0);
111572
- return node;
111573
- }));
111574
- for (let i2 = 0;i2 < results.length; i2++) {
111575
- const result = results[i2];
111576
- const node = nodes[i2];
111577
- if (!node || result.status === "fulfilled")
111578
- continue;
111579
- tickHadFailure = true;
111580
- options.onError?.(result.reason);
111581
- const prevFailures = nodeFailureCounts.get(node.workTree) ?? 0;
111582
- const nextFailures = prevFailures + 1;
111583
- nodeFailureCounts.set(node.workTree, nextFailures);
111584
- maxNodeFailures = Math.max(maxNodeFailures, nextFailures);
111931
+ for (const node of nodes) {
111932
+ try {
111933
+ const prev = state.get(node.workTree) ?? {
111934
+ prevEntries: [],
111935
+ prevHead: { head: null },
111936
+ baselineEstablished: false
111937
+ };
111938
+ const result = await pollGitWorkspaceNode({
111939
+ options,
111940
+ node,
111941
+ prev
111942
+ });
111943
+ allEvents.push(...result.events);
111944
+ if (result.needsReconcile)
111945
+ needsReconcile = true;
111946
+ state.set(node.workTree, result.nextState);
111947
+ nodeFailureCounts.set(node.workTree, 0);
111948
+ } catch (reason) {
111949
+ tickHadFailure = true;
111950
+ options.onError?.(reason);
111951
+ const prevFailures = nodeFailureCounts.get(node.workTree) ?? 0;
111952
+ const nextFailures = prevFailures + 1;
111953
+ nodeFailureCounts.set(node.workTree, nextFailures);
111954
+ maxNodeFailures = Math.max(maxNodeFailures, nextFailures);
111955
+ }
111585
111956
  }
111586
111957
  if (tickHadFailure) {
111587
111958
  consecutiveFailureTicks += 1;
@@ -111639,7 +112010,7 @@ function createGitWorkspaceChangeSource(options) {
111639
112010
  }
111640
112011
  };
111641
112012
  }
111642
- var DEFAULT_POLL_INTERVAL_MS = 1000, MAX_BACKOFF_MS = 30000, PERSISTENT_FAILURE_THRESHOLD = 3;
112013
+ var DEFAULT_POLL_INTERVAL_MS = 3000, MAX_BACKOFF_MS = 30000, PERSISTENT_FAILURE_THRESHOLD = 3;
111643
112014
  var init_git_workspace_change_source = __esm(() => {
111644
112015
  init_git_workspace_porcelain();
111645
112016
  });
@@ -112068,9 +112439,9 @@ class NodeFsHandler {
112068
112439
  if (this.fsw.closed) {
112069
112440
  return;
112070
112441
  }
112071
- const dirname11 = sp.dirname(file2);
112442
+ const dirname12 = sp.dirname(file2);
112072
112443
  const basename4 = sp.basename(file2);
112073
- const parent = this.fsw._getWatchedDir(dirname11);
112444
+ const parent = this.fsw._getWatchedDir(dirname12);
112074
112445
  let prevStats = stats;
112075
112446
  if (parent.has(basename4))
112076
112447
  return;
@@ -112097,7 +112468,7 @@ class NodeFsHandler {
112097
112468
  prevStats = newStats2;
112098
112469
  }
112099
112470
  } catch (error51) {
112100
- this.fsw._remove(dirname11, basename4);
112471
+ this.fsw._remove(dirname12, basename4);
112101
112472
  }
112102
112473
  } else if (parent.has(basename4)) {
112103
112474
  const at = newStats.atimeMs;
@@ -113486,8 +113857,8 @@ function diffPathIndexes(previous, next4) {
113486
113857
  // src/infrastructure/services/workspace/workspace-sync-state.ts
113487
113858
  import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
113488
113859
  import * as fs11 from "node:fs/promises";
113489
- import { homedir as homedir8 } from "node:os";
113490
- import { join as join24 } from "node:path";
113860
+ import { homedir as homedir9 } from "node:os";
113861
+ import { join as join25 } from "node:path";
113491
113862
  function workspaceKeyFor(workingDir) {
113492
113863
  const normalized = normalizeWorkingDirForLookup(workingDir);
113493
113864
  return createHash7("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -113501,10 +113872,10 @@ function buildPathIndex(entries2) {
113501
113872
  }
113502
113873
  function manifestPath(machineId, workingDir) {
113503
113874
  const key = workspaceKeyFor(workingDir);
113504
- return join24(SYNC_STATE_DIR, machineId, key, "manifest.json");
113875
+ return join25(SYNC_STATE_DIR, machineId, key, "manifest.json");
113505
113876
  }
113506
113877
  async function ensureDir(filePath) {
113507
- await fs11.mkdir(join24(filePath, ".."), { recursive: true, mode: 448 });
113878
+ await fs11.mkdir(join25(filePath, ".."), { recursive: true, mode: 448 });
113508
113879
  }
113509
113880
  async function loadWorkspaceSyncManifest(machineId, workingDir) {
113510
113881
  try {
@@ -113564,7 +113935,7 @@ function entriesFromPathIndex(paths) {
113564
113935
  }
113565
113936
  var SYNC_STATE_VERSION = "2", SYNC_STATE_DIR;
113566
113937
  var init_workspace_sync_state = __esm(() => {
113567
- SYNC_STATE_DIR = join24(homedir8(), ".chatroom", "sync-state");
113938
+ SYNC_STATE_DIR = join25(homedir9(), ".chatroom", "sync-state");
113568
113939
  });
113569
113940
 
113570
113941
  // src/infrastructure/services/workspace/workspace-file-tree-coordinator.ts
@@ -113941,12 +114312,12 @@ var init_durable_coalescing_state_schema = __esm(() => {
113941
114312
  });
113942
114313
 
113943
114314
  // src/daemon/infrastructure/outbox/lib/durable-coalescing-state-store.ts
113944
- import { mkdirSync as mkdirSync6 } from "node:fs";
113945
- import { dirname as dirname12 } from "node:path";
113946
- import { DatabaseSync } from "node:sqlite";
114315
+ import { mkdirSync as mkdirSync7 } from "node:fs";
114316
+ import { dirname as dirname13 } from "node:path";
114317
+ import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
113947
114318
  function openDurableCoalescingStateStore(path8) {
113948
- mkdirSync6(dirname12(path8), { recursive: true });
113949
- const db = new DatabaseSync(path8);
114319
+ mkdirSync7(dirname13(path8), { recursive: true });
114320
+ const db = new DatabaseSync2(path8);
113950
114321
  for (const sql of DURABLE_COALESCING_STATE_MIGRATIONS)
113951
114322
  db.exec(sql);
113952
114323
  db.prepare("UPDATE coalescing_outbox_entries SET status='pending' WHERE status='in_flight'").run();
@@ -113963,13 +114334,6 @@ var init_durable_coalescing_state_store = __esm(() => {
113963
114334
  init_durable_coalescing_state_schema();
113964
114335
  });
113965
114336
 
113966
- // src/daemon/infrastructure/outbox/lib/outbox-retry-backoff.ts
113967
- function computeExponentialRetryDelayMs(attempt, retryDelayMs, maxRetryDelayMs) {
113968
- const exponent = Math.min(Math.max(0, attempt), MAX_RETRY_EXPONENT);
113969
- return Math.min(retryDelayMs * 2 ** exponent, maxRetryDelayMs);
113970
- }
113971
- var MAX_RETRY_EXPONENT = 20;
113972
-
113973
114337
  // src/daemon/infrastructure/outbox/lib/coalescing-state-outbox.ts
113974
114338
  function createCoalescingStateOutbox(options) {
113975
114339
  const durable = [options.store, options.deliveryKey, options.serialize, options.deserialize];
@@ -114146,14 +114510,6 @@ var init_keyed_coalescing_state_outbox_registry = __esm(() => {
114146
114510
  init_coalescing_state_outbox();
114147
114511
  });
114148
114512
 
114149
- // src/daemon/infrastructure/outbox/outbox-db-path.ts
114150
- import { homedir as homedir9 } from "node:os";
114151
- import { join as join25 } from "node:path";
114152
- function resolveOutboxDbPath(machineId, kind) {
114153
- return join25(homedir9(), ".chatroom", "daemon", machineId, `outbox-${kind}.sqlite`);
114154
- }
114155
- var init_outbox_db_path = () => {};
114156
-
114157
114513
  // src/daemon/infrastructure/outbox/workspace-file-tree-checkpoint-outbox.ts
114158
114514
  function createWorkspaceFileTreeCheckpointOutboxRegistry(machineId, createSend, options) {
114159
114515
  return createKeyedCoalescingStateOutboxRegistry({
@@ -114175,193 +114531,6 @@ var init_workspace_file_tree_checkpoint_outbox = __esm(() => {
114175
114531
  WORKSPACE_FILE_TREE_CHECKPOINT_OUTBOX_MAX_RETRY_DELAY_MS = 5 * 60000;
114176
114532
  });
114177
114533
 
114178
- // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-schema.ts
114179
- var DURABLE_FIFO_QUEUE_MIGRATIONS;
114180
- var init_durable_fifo_queue_schema = __esm(() => {
114181
- DURABLE_FIFO_QUEUE_MIGRATIONS = [
114182
- `CREATE TABLE IF NOT EXISTS schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`,
114183
- `CREATE TABLE IF NOT EXISTS fifo_outbox_entries (id INTEGER PRIMARY KEY AUTOINCREMENT, delivery_key TEXT NOT NULL, payload_json TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', attempts INTEGER NOT NULL DEFAULT 0, last_error TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL)`,
114184
- `CREATE INDEX IF NOT EXISTS idx_fifo_outbox_pending ON fifo_outbox_entries(delivery_key, status, id)`
114185
- ];
114186
- });
114187
-
114188
- // src/daemon/infrastructure/outbox/lib/durable-fifo-queue-store.ts
114189
- import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
114190
- import { mkdirSync as mkdirSync7 } from "node:fs";
114191
- import { dirname as dirname13 } from "node:path";
114192
- function openDurableFifoQueueStore(dbPath) {
114193
- mkdirSync7(dirname13(dbPath), { recursive: true });
114194
- const db = new DatabaseSync2(dbPath);
114195
- for (const sql of DURABLE_FIFO_QUEUE_MIGRATIONS)
114196
- db.exec(sql);
114197
- db.prepare("UPDATE fifo_outbox_entries SET status='pending', updated_at=? WHERE status='in_flight'").run(Date.now());
114198
- return {
114199
- enqueue: (key, payloadJson) => {
114200
- const now = Date.now();
114201
- return Number(db.prepare("INSERT INTO fifo_outbox_entries(delivery_key,payload_json,created_at,updated_at) VALUES(?,?,?,?) RETURNING id").get(key, payloadJson, now, now).id);
114202
- },
114203
- claimNextBatch: (key, limit) => db.prepare("UPDATE fifo_outbox_entries SET status='in_flight', attempts=attempts+1, updated_at=? WHERE id IN (SELECT id FROM fifo_outbox_entries WHERE delivery_key=? AND status='pending' ORDER BY id LIMIT ?) RETURNING id, delivery_key as deliveryKey, payload_json as payloadJson, attempts").all(Date.now(), key, limit),
114204
- markDone: (id3) => {
114205
- db.prepare("DELETE FROM fifo_outbox_entries WHERE id=?").run(id3);
114206
- },
114207
- markPending: (id3) => {
114208
- db.prepare("UPDATE fifo_outbox_entries SET status='pending', last_error=NULL, updated_at=? WHERE id=?").run(Date.now(), id3);
114209
- },
114210
- updatePayload: (id3, payloadJson) => {
114211
- db.prepare("UPDATE fifo_outbox_entries SET payload_json=?, updated_at=? WHERE id=?").run(payloadJson, Date.now(), id3);
114212
- },
114213
- markPendingRetry: (id3, error51) => {
114214
- db.prepare("UPDATE fifo_outbox_entries SET status='pending', last_error=?, updated_at=? WHERE id=?").run(String(error51), Date.now(), id3);
114215
- },
114216
- listPendingForRecovery: (key) => db.prepare("SELECT id, delivery_key as deliveryKey, payload_json as payloadJson, attempts FROM fifo_outbox_entries WHERE delivery_key=? AND status IN ('pending','in_flight') ORDER BY id").all(key),
114217
- close: () => db.close()
114218
- };
114219
- }
114220
- var init_durable_fifo_queue_store = __esm(() => {
114221
- init_durable_fifo_queue_schema();
114222
- });
114223
-
114224
- // src/daemon/infrastructure/outbox/lib/fifo-batched-outbox.ts
114225
- function createFifoBatchedOutbox(o) {
114226
- const waiters = new Map;
114227
- let running3;
114228
- let stopped = false;
114229
- let retryTimer;
114230
- let retryAttempt = 0;
114231
- const retryDelay = () => computeExponentialRetryDelayMs(retryAttempt++, o.retryDelayMs ?? 500, o.maxRetryDelayMs ?? 5000);
114232
- const scheduleRetry = () => {
114233
- if (!stopped && !retryTimer) {
114234
- retryTimer = setTimeout(() => {
114235
- retryTimer = undefined;
114236
- drain2().catch(() => {
114237
- return;
114238
- });
114239
- }, retryDelay());
114240
- retryTimer.unref?.();
114241
- }
114242
- };
114243
- const drain2 = async () => {
114244
- if (running3)
114245
- return running3;
114246
- running3 = (async () => {
114247
- while (true) {
114248
- const rows = o.store.claimNextBatch(o.deliveryKey, o.batchSize);
114249
- if (!rows.length)
114250
- break;
114251
- try {
114252
- const results = await o.send(rows.map((r) => o.deserialize(r.payloadJson)));
114253
- let hasRetry = false;
114254
- for (let i2 = 0;i2 < results.length; i2++) {
114255
- const item = o.deserialize(rows[i2].payloadJson);
114256
- const outcome = o.classifyOutcome?.(results[i2], item) ?? { kind: "success" };
114257
- if (outcome.kind === "retry") {
114258
- o.store.updatePayload(rows[i2].id, o.serialize(outcome.item));
114259
- o.store.markPending(rows[i2].id);
114260
- hasRetry = true;
114261
- continue;
114262
- }
114263
- o.store.markDone(rows[i2].id);
114264
- waiters.get(rows[i2].id)?.resolve(results[i2]);
114265
- waiters.delete(rows[i2].id);
114266
- }
114267
- for (let i2 = results.length;i2 < rows.length; i2++)
114268
- o.store.markPending(rows[i2].id);
114269
- if (hasRetry) {
114270
- scheduleRetry();
114271
- break;
114272
- }
114273
- retryAttempt = 0;
114274
- } catch (e) {
114275
- o.onError?.(e);
114276
- for (const r of rows)
114277
- o.store.markPendingRetry(r.id, e);
114278
- scheduleRetry();
114279
- break;
114280
- }
114281
- }
114282
- })().finally(() => {
114283
- running3 = undefined;
114284
- });
114285
- return running3;
114286
- };
114287
- const enqueue = (item) => {
114288
- if (stopped)
114289
- return Promise.reject(new Error("Outbox is stopped"));
114290
- const id3 = o.store.enqueue(o.deliveryKey, o.serialize(item));
114291
- const p = new Promise((resolve8, reject) => waiters.set(id3, { resolve: resolve8, reject }));
114292
- drain2().catch(() => {
114293
- return;
114294
- });
114295
- return p;
114296
- };
114297
- const flushNow = async () => {
114298
- while (o.store.listPendingForRecovery(o.deliveryKey).length) {
114299
- await drain2();
114300
- }
114301
- };
114302
- const stop4 = async () => {
114303
- if (stopped)
114304
- return;
114305
- if (retryTimer)
114306
- clearTimeout(retryTimer);
114307
- retryTimer = undefined;
114308
- await flushNow();
114309
- stopped = true;
114310
- };
114311
- return { enqueue, flushNow, stop: stop4 };
114312
- }
114313
- var init_fifo_batched_outbox = () => {};
114314
-
114315
- // src/daemon/infrastructure/outbox/lib/keyed-fifo-batched-outbox-registry.ts
114316
- function createKeyedFifoBatchedOutboxRegistry(o) {
114317
- const boxes = new Map;
114318
- const get13 = (key) => {
114319
- let b = boxes.get(key);
114320
- if (!b) {
114321
- b = createFifoBatchedOutbox({
114322
- batchSize: o.batchSize,
114323
- send: o.createSend(key),
114324
- store: o.store,
114325
- deliveryKey: key,
114326
- serialize: o.serialize,
114327
- deserialize: o.deserialize,
114328
- retryDelayMs: o.retryDelayMs,
114329
- maxRetryDelayMs: o.maxRetryDelayMs,
114330
- onError: (e) => o.onError?.(key, e),
114331
- classifyOutcome: o.classifyOutcome
114332
- });
114333
- boxes.set(key, b);
114334
- }
114335
- return b;
114336
- };
114337
- const stop4 = async (k) => {
114338
- const b = boxes.get(k);
114339
- if (b) {
114340
- await b.stop();
114341
- boxes.delete(k);
114342
- }
114343
- };
114344
- return {
114345
- enqueue: (k, i2) => get13(k).enqueue(i2),
114346
- flushNow: async (k) => {
114347
- if (k)
114348
- await get13(k).flushNow();
114349
- else
114350
- for (const b of boxes.values())
114351
- await b.flushNow();
114352
- },
114353
- stop: stop4,
114354
- stopAll: async () => {
114355
- for (const k of [...boxes.keys()])
114356
- await stop4(k);
114357
- o.store.close();
114358
- }
114359
- };
114360
- }
114361
- var init_keyed_fifo_batched_outbox_registry = __esm(() => {
114362
- init_fifo_batched_outbox();
114363
- });
114364
-
114365
114534
  // src/daemon/infrastructure/outbox/workspace-file-tree-delta-outbox.ts
114366
114535
  function createWorkspaceFileTreeDeltaOutboxRegistry(machineId, createSend, options) {
114367
114536
  const store = openDurableFifoQueueStore(resolveOutboxDbPath(machineId, "file-tree-delta"));
@@ -114396,6 +114565,8 @@ var init_workspace_file_tree_delta_outbox = __esm(() => {
114396
114565
  var init_outbox = __esm(() => {
114397
114566
  init_workspace_file_tree_checkpoint_outbox();
114398
114567
  init_workspace_file_tree_delta_outbox();
114568
+ init_agent_lifecycle_outbox();
114569
+ init_agent_lifecycle_send();
114399
114570
  });
114400
114571
 
114401
114572
  // ../../services/backend/src/domain/workspace-file-tree/types.ts
@@ -115804,7 +115975,8 @@ var init_command_run_subscription = __esm(() => {
115804
115975
  // src/daemon/entry/task-delivery/task-delivery-logic.ts
115805
115976
  function isPendingAliveRunningTask(task) {
115806
115977
  const { agentConfig, status: status3 } = task;
115807
- return status3 === "pending" && agentConfig.spawnedAgentPid != null && isAgentDesiredRunning(agentConfig.desiredState);
115978
+ const op = getNativeDeliverySession()?.agentOperationalReadModel?.get(task.chatroomId, agentConfig.role);
115979
+ return status3 === "pending" && (agentConfig.spawnedAgentPid != null || op?.isAlive === true) && isOperationalDesiredRunning(op);
115808
115980
  }
115809
115981
  function isSlotUnavailableForPid(slot, pid, isPidAlive, now = Date.now()) {
115810
115982
  if (!slot) {
@@ -115847,7 +116019,8 @@ function isNativeAgentSlotDown(task, health, now = Date.now()) {
115847
116019
  function isNativeActiveTaskAgentDown(task, health, now) {
115848
116020
  if (!isNativeHarness(task.agentConfig.agentHarness))
115849
116021
  return false;
115850
- if (!isAgentDesiredRunning(task.agentConfig.desiredState))
116022
+ const op = getNativeDeliverySession()?.agentOperationalReadModel?.get(task.chatroomId, task.agentConfig.role);
116023
+ if (!isOperationalDesiredRunning(op))
115851
116024
  return false;
115852
116025
  if (!isNativeRevivableTaskStatus(task))
115853
116026
  return false;
@@ -115871,7 +116044,8 @@ function isNativePendingTaskNeedingWake(task) {
115871
116044
  return false;
115872
116045
  if (task.status !== "pending")
115873
116046
  return false;
115874
- if (isAgentDesiredRunning(task.agentConfig.desiredState))
116047
+ const op = getNativeDeliverySession()?.agentOperationalReadModel?.get(task.chatroomId, task.agentConfig.role);
116048
+ if (isOperationalDesiredRunning(op))
115875
116049
  return false;
115876
116050
  return Boolean(task.agentConfig.workingDir);
115877
116051
  }
@@ -115947,10 +116121,10 @@ function listTasksReadyForNudge(tasks, now, cooldown, getSlot) {
115947
116121
  }
115948
116122
  var PENDING_IDLE_NUDGE_MS = 15000, NUDGE_COOLDOWN_MS = 60000;
115949
116123
  var init_task_delivery_logic = __esm(() => {
115950
- init_assigned_task();
115951
116124
  init_check_agent_slot();
115952
116125
  init_predicates();
115953
116126
  init_agent_process_manager();
116127
+ init_native_delivery_session_registry();
115954
116128
  init_native_ready_invariant();
115955
116129
  init_native_task_injector_logic();
115956
116130
  });
@@ -116050,7 +116224,7 @@ function runNativeWakeEffect(task, runtime4, effectContext2, agentMgr) {
116050
116224
  if (!ctx)
116051
116225
  return;
116052
116226
  const { chatroomId, agentConfig, role, workingDir, wantResume } = ctx;
116053
- console.log(`[TaskMonitor] native wake ${role}@${chatroomId} — desiredState=stopped with pending task ${task.taskId}`);
116227
+ console.log(`[TaskMonitor] native wake ${role}@${chatroomId} — operational_state=stopped with pending task ${task.taskId}`);
116054
116228
  exports_Runtime.runFork(runtime4)(exports_Effect.gen(function* () {
116055
116229
  yield* agentMgr.ensureRunning({
116056
116230
  chatroomId,
@@ -116152,6 +116326,31 @@ var init_task_delivery_processor = __esm(() => {
116152
116326
  init_task_delivery_logic();
116153
116327
  });
116154
116328
 
116329
+ // src/daemon/infrastructure/agent-operational/enrich-snapshot-with-operational.ts
116330
+ function enrichSnapshotWithOperational(snapshot) {
116331
+ const op = getNativeDeliverySession()?.agentOperationalReadModel?.get(snapshot.chatroomId, snapshot.agentConfig.role);
116332
+ if (!op)
116333
+ return snapshot;
116334
+ return { ...snapshot, agentConfig: { ...snapshot.agentConfig, desiredState: isOperationalDesiredRunning(op) ? "running" : "stopped" } };
116335
+ }
116336
+ function enrichSnapshotsWithOperational(snapshots) {
116337
+ return snapshots.map(enrichSnapshotWithOperational);
116338
+ }
116339
+ var init_enrich_snapshot_with_operational = __esm(() => {
116340
+ init_native_delivery_session_registry();
116341
+ });
116342
+
116343
+ // src/daemon/infrastructure/agent-operational/subscribe-machine-agent-operational-status.ts
116344
+ function subscribeMachineAgentOperationalStatus(client4, options, onRows) {
116345
+ return client4.onUpdate(api.machines.subscribeMachineAgentOperationalStatus, { sessionId: options.sessionId, machineId: options.machineId }, (result) => {
116346
+ if (Array.isArray(result))
116347
+ onRows(result);
116348
+ });
116349
+ }
116350
+ var init_subscribe_machine_agent_operational_status = __esm(() => {
116351
+ init_api3();
116352
+ });
116353
+
116155
116354
  // src/daemon/infrastructure/inbox/fetch-machine-assigned-task-snapshots.ts
116156
116355
  async function fetchMachineAssignedTaskSnapshots(sessionDeps, machineId) {
116157
116356
  const result = await sessionDeps.backend.query(api.machines.listMachineAssignedTaskSnapshots, {
@@ -116321,11 +116520,12 @@ async function handleTaskInboxUpdate(update6, deps) {
116321
116520
  taskSnapshotState?.applySignalPage(update6.signals, update6.snapshots);
116322
116521
  if (update6.snapshots.length === 0)
116323
116522
  return;
116324
- await processTasksUpdate(deps.runtime, deps.effectContext, deps.cooldown, deps.agentMgr, deps.sessionDeps, deps.machineId, "inbox-signal", { snapshots: update6.snapshots });
116523
+ await processTasksUpdate(deps.runtime, deps.effectContext, deps.cooldown, deps.agentMgr, deps.sessionDeps, deps.machineId, "inbox-signal", { snapshots: enrichSnapshotsWithOperational(update6.snapshots) });
116325
116524
  }
116326
116525
  var init_task_inbox_delivery = __esm(() => {
116327
116526
  init_native_delivery_session_registry();
116328
116527
  init_task_delivery_processor();
116528
+ init_enrich_snapshot_with_operational();
116329
116529
  });
116330
116530
 
116331
116531
  // src/daemon/infrastructure/inbox/task.ts
@@ -116435,6 +116635,10 @@ var init_task = __esm(() => {
116435
116635
 
116436
116636
  // src/daemon/entry/task-inbox-runtime.ts
116437
116637
  async function bootstrapMachineAssignedTaskSnapshots(deps) {
116638
+ await deps.sessionDeps.backend.mutation(api.machines.backfillAgentOperationalStatusForMachine, {
116639
+ sessionId: deps.sessionDeps.sessionId,
116640
+ machineId: deps.machineId
116641
+ });
116438
116642
  await deps.sessionDeps.backend.mutation(api.machines.syncMachineAssignedTaskSnapshotsMutation, {
116439
116643
  sessionId: deps.sessionDeps.sessionId,
116440
116644
  machineId: deps.machineId
@@ -116443,7 +116647,7 @@ async function bootstrapMachineAssignedTaskSnapshots(deps) {
116443
116647
  deps.taskSnapshotState?.replace(tasks);
116444
116648
  if (!tasks.length)
116445
116649
  return;
116446
- await processTasksUpdate(deps.runtime, deps.effectContext, deps.cooldown, deps.agentMgr, deps.sessionDeps, deps.machineId, "bootstrap", { snapshots: tasks });
116650
+ await processTasksUpdate(deps.runtime, deps.effectContext, deps.cooldown, deps.agentMgr, deps.sessionDeps, deps.machineId, "bootstrap", { snapshots: enrichSnapshotsWithOperational(tasks) });
116447
116651
  }
116448
116652
  function isAbortError(error51) {
116449
116653
  return error51 instanceof Error && error51.name === "AbortError";
@@ -116487,13 +116691,15 @@ var NATIVE_DELIVERY_RECONCILE_MS = 1e4, INBOX_RESTART_INITIAL_MS = 1000, INBOX_R
116487
116691
  const abort = new AbortController;
116488
116692
  let stopped = false;
116489
116693
  const taskSnapshotState = new MachineTaskSnapshotState;
116694
+ const agentOperationalReadModel = new AgentOperationalReadModel;
116490
116695
  registerNativeDeliverySession({
116491
116696
  runtime: runtime4,
116492
116697
  effectContext: effectContext2,
116493
116698
  agentMgr,
116494
116699
  sessionDeps,
116495
116700
  machineId: session2.machineId,
116496
- taskSnapshotState
116701
+ taskSnapshotState,
116702
+ agentOperationalReadModel
116497
116703
  });
116498
116704
  const cooldown = new RecoveryCooldown;
116499
116705
  let inboxUpdateInFlight = false;
@@ -116535,11 +116741,24 @@ var NATIVE_DELIVERY_RECONCILE_MS = 1e4, INBOX_RESTART_INITIAL_MS = 1000, INBOX_R
116535
116741
  console.warn("[TaskInbox] bootstrap failed:", error51);
116536
116742
  return exports_Effect.void;
116537
116743
  }));
116744
+ const unsubscribeOperational = subscribeMachineAgentOperationalStatus(wsClient2, {
116745
+ sessionId: session2.sessionId,
116746
+ machineId: session2.machineId,
116747
+ signal: abort.signal
116748
+ }, (rows) => {
116749
+ if (stopped)
116750
+ return;
116751
+ const changed = agentOperationalReadModel.replace(rows);
116752
+ const snapshots = changed.flatMap(({ chatroomId, role }) => taskSnapshotState.listForRole(chatroomId, role));
116753
+ if (snapshots.length === 0)
116754
+ return;
116755
+ processTasksUpdate(runtime4, effectContext2, cooldown, agentMgr, sessionDeps, session2.machineId, "operational-status", { snapshots: enrichSnapshotsWithOperational(snapshots) }).catch((error51) => console.warn("[TaskInbox] operational-status reconcile failed:", error51));
116756
+ }) ?? (() => {});
116538
116757
  const reconcileTimer = setInterval(() => {
116539
116758
  if (stopped || inboxUpdateInFlight || reconcileInFlight)
116540
116759
  return;
116541
116760
  reconcileInFlight = true;
116542
- processTasksUpdate(runtime4, effectContext2, cooldown, agentMgr, sessionDeps, session2.machineId, "periodic-reconcile", { snapshots: taskSnapshotState.listAll() }).catch((error51) => {
116761
+ processTasksUpdate(runtime4, effectContext2, cooldown, agentMgr, sessionDeps, session2.machineId, "periodic-reconcile", { snapshots: enrichSnapshotsWithOperational(taskSnapshotState.listAll()) }).catch((error51) => {
116543
116762
  console.warn("[TaskInbox] local delivery reconciliation failed:", error51);
116544
116763
  }).finally(() => {
116545
116764
  reconcileInFlight = false;
@@ -116549,6 +116768,7 @@ var NATIVE_DELIVERY_RECONCILE_MS = 1e4, INBOX_RESTART_INITIAL_MS = 1000, INBOX_R
116549
116768
  return {
116550
116769
  stop() {
116551
116770
  stopped = true;
116771
+ unsubscribeOperational();
116552
116772
  abort.abort();
116553
116773
  clearInterval(reconcileTimer);
116554
116774
  unregisterNativeDeliverySession();
@@ -116563,6 +116783,8 @@ var init_task_inbox_runtime = __esm(() => {
116563
116783
  init_native_delivery_session_registry();
116564
116784
  init_task_delivery_processor();
116565
116785
  init_task_delivery_logic();
116786
+ init_enrich_snapshot_with_operational();
116787
+ init_subscribe_machine_agent_operational_status();
116566
116788
  init_fetch_machine_assigned_task_snapshots();
116567
116789
  init_inbox();
116568
116790
  init_task_inbox_delivery();
@@ -117020,6 +117242,9 @@ function createDaemonRuntime(deps) {
117020
117242
  shutdownWatchdog.unref?.();
117021
117243
  if (heartbeatTimer)
117022
117244
  clearInterval(heartbeatTimer);
117245
+ await deps.agentLifecycleOutbox?.stopAll().catch(() => {
117246
+ return;
117247
+ });
117023
117248
  stopWorkers();
117024
117249
  await withTimeout2(exports_Effect.runPromise(exports_Effect.gen(function* () {
117025
117250
  const effectContext2 = yield* exports_Effect.context();
@@ -132841,7 +133066,11 @@ async function startDaemon() {
132841
133066
  console.log(`[daemon] Local web UI: http://127.0.0.1:${localWeb.port}/`);
132842
133067
  const layers = daemonSessionToLayers(init2);
132843
133068
  startBackgroundMachineCapabilitiesDiscovery(createStartBackgroundCapabilitiesDiscoveryDeps(layers));
132844
- const runtime4 = createDaemonRuntime({ wsClient: wsClient2, layers });
133069
+ const runtime4 = createDaemonRuntime({
133070
+ wsClient: wsClient2,
133071
+ layers,
133072
+ agentLifecycleOutbox: init2.agentLifecycleOutbox
133073
+ });
132845
133074
  try {
132846
133075
  await runtime4.run();
132847
133076
  } finally {
@@ -134086,4 +134315,4 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
134086
134315
  });
134087
134316
  program2.parse();
134088
134317
 
134089
- //# debugId=D3E73C85EB221DCE64756E2164756E21
134318
+ //# debugId=6F8892900A69B55764756E2164756E21