@adhdev/mesh-shared 1.0.60-rc.40 → 1.0.60-rc.42

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.
@@ -87,7 +87,7 @@ export type NoProgressObservedStatus = SessionStatus | 'unknown';
87
87
  * Reasons a turn hold exists. Holds are attempt-scoped only (C2: delivery-side
88
88
  * waits are consumer deferral on the `turn.deliver` cursor, not hold rows).
89
89
  */
90
- export declare const HOLD_REASONS: readonly ["await_delivery", "await_consume", "await_turn", "liveness", "hard_ceiling", "live_pending", "transcript_quiet", "weak_candidate", "suspension_before_consumed"];
90
+ export declare const HOLD_REASONS: readonly ["await_delivery", "await_consume", "await_turn", "liveness", "hard_ceiling", "live_pending", "transcript_quiet", "weak_candidate", "suspension_before_consumed", "await_report"];
91
91
  export type HoldReason = typeof HOLD_REASONS[number];
92
92
  export declare const TURN_OUTCOMES: readonly ["completed", "failed", "cancelled"];
93
93
  export type TurnOutcome = typeof TURN_OUTCOMES[number];
@@ -207,6 +207,13 @@ export type TurnEvidenceBody = {
207
207
  releasedByHardCap?: boolean;
208
208
  nativeOutcome?: NativeTurnOutcome;
209
209
  live?: LiveTurnPending;
210
+ /**
211
+ * Stamped (true only) by the WORKER's daemon when the session holds a live
212
+ * worker-MCP session bind, i.e. the worker can call report_completion. The
213
+ * owner then treats a genuine end as a report-awaiting candidate (R9r), not
214
+ * a commit. Absent = no reporting surface = today's genuine-end commit.
215
+ */
216
+ reportExpected?: boolean;
210
217
  } | {
211
218
  kind: 'transcript_final';
212
219
  selfAttributing: boolean;
@@ -327,7 +334,7 @@ export declare const isEvidenceSourceId: (value: unknown) => value is "fsm_edge"
327
334
  export declare const isTurnEvidenceKind: (value: unknown) => value is "no_progress" | "delivered" | "liveness" | "turn_end" | "transcript_final" | "session_error" | "dispatch_failed" | "dispatch_accepted" | "delivery_refused" | "duplicate_dispatch_refusal" | "session_rebound" | "turn_started" | "suspension" | "suspension_resolved" | "transcript_activity" | "process_exit" | "worker_report" | "worker_progress" | "git_side_effect" | "cancel" | "operator_status" | "coordinator_ack" | "hold_expired";
328
335
  export declare const isTurnReason: (value: unknown) => value is "intentional_cleanup" | "operator_cancel" | "task_removed" | "mission_abandoned" | "superseded" | "operator_update" | "refine_terminal" | "validation_terminal" | "hard_ceiling" | "turn_end" | "weak_end_confirmed" | "transcript_final" | "worker_reported" | "finalization_timeout_no_response" | "session_error" | "provider_auth_failed" | "provider_billing_failed" | "reclaim_budget_exhausted" | "hollow_max_retries" | "migration_orphan" | "dispatch_refused_session_exited" | "dispatch_refused_no_target" | "dispatch_refused_unsupported_input" | "dispatch_failed" | "session_exit" | "session_exit_before_turn" | "session_dead" | "hollow_completion" | "assigned_stranded_dispatch_unconfirmed" | "delivered_not_consumed_redrive" | "delivered_no_turn_deadline";
329
336
  export declare const isNotifyKind: (value: unknown) => value is "stopped" | "failed" | "no_progress" | "completed" | "cancelled" | "approval" | "choice" | "approval_resolved" | "candidate" | "progress" | "late_completion" | "mesh_event";
330
- export declare const isHoldReason: (value: unknown) => value is "await_delivery" | "await_consume" | "await_turn" | "liveness" | "hard_ceiling" | "live_pending" | "transcript_quiet" | "weak_candidate" | "suspension_before_consumed";
337
+ export declare const isHoldReason: (value: unknown) => value is "await_delivery" | "await_consume" | "await_turn" | "liveness" | "hard_ceiling" | "live_pending" | "transcript_quiet" | "weak_candidate" | "suspension_before_consumed" | "await_report";
331
338
  export declare const isTurnOutcome: (value: unknown) => value is "failed" | "completed" | "cancelled";
332
339
  export declare const isCommitStrength: (value: unknown) => value is "operator" | "genuine" | "weak" | "tool_report";
333
340
  export declare function isEvidenceIdentifier(value: unknown): value is string;
package/dist/types.d.ts CHANGED
@@ -136,4 +136,15 @@ export interface RepoMeshSessionStatus {
136
136
  startedAt?: string | null;
137
137
  lastActivityAt?: string | null;
138
138
  isCached?: boolean;
139
+ /**
140
+ * Worker MCP delivery outcome for this session's launch (present only for a
141
+ * mesh-delegated launch). `reason` is a short enum code, never free text —
142
+ * see `WORKER_MCP_DELIVERY_REASONS` (daemon-core's `mesh/worker-mcp-isolation.ts`,
143
+ * the source of the vocabulary this field's values are drawn from; not
144
+ * re-exported here so mesh-shared stays a dependency-free leaf).
145
+ */
146
+ workerMcp?: {
147
+ delivered: boolean;
148
+ reason?: string;
149
+ };
139
150
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adhdev/mesh-shared",
3
- "version": "1.0.60-rc.40",
3
+ "version": "1.0.60-rc.42",
4
4
  "description": "ADHDev mesh-shared — pure mesh/git status normalizers shared by daemon-core and web-core",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -122,6 +122,11 @@ export type NoProgressObservedStatus = SessionStatus | 'unknown'
122
122
  export const HOLD_REASONS = [
123
123
  'await_delivery', 'await_consume', 'await_turn', 'liveness', 'hard_ceiling',
124
124
  'live_pending', 'transcript_quiet', 'weak_candidate', 'suspension_before_consumed',
125
+ // A genuine FSM end of a mesh turn whose worker holds a live worker-MCP bind:
126
+ // the structured report is the primary evidence (design §F2), so the idle edge
127
+ // only opens this hold (R9r) — a report commits (R17), a new busy edge cancels
128
+ // it as a false idle (R12r), expiry commits weak (R13r).
129
+ 'await_report',
125
130
  ] as const
126
131
  export type HoldReason = typeof HOLD_REASONS[number]
127
132
 
@@ -211,7 +216,14 @@ export type TurnEvidenceBody =
211
216
  | { kind: 'suspension'; modal: SuspensionModal; modalKey?: string }
212
217
  | { kind: 'suspension_resolved'; resolution: SuspensionResolution; via: SuspensionResolutionVia }
213
218
  | { kind: 'turn_end'; strength: TurnEndStrength; afterFinalizationTimeout?: boolean; hollow?: boolean; summary?: SummaryRef
214
- blockReason?: TurnEndBlockReason; releasedByHardCap?: boolean; nativeOutcome?: NativeTurnOutcome; live?: LiveTurnPending }
219
+ blockReason?: TurnEndBlockReason; releasedByHardCap?: boolean; nativeOutcome?: NativeTurnOutcome; live?: LiveTurnPending
220
+ /**
221
+ * Stamped (true only) by the WORKER's daemon when the session holds a live
222
+ * worker-MCP session bind, i.e. the worker can call report_completion. The
223
+ * owner then treats a genuine end as a report-awaiting candidate (R9r), not
224
+ * a commit. Absent = no reporting surface = today's genuine-end commit.
225
+ */
226
+ reportExpected?: boolean }
215
227
  | { kind: 'transcript_final'; selfAttributing: boolean; nativeRead: boolean; nativeMarker?: NativeTurnMarkerRef
216
228
  live: LiveTurnPending; summary?: SummaryRef; messageAt?: number }
217
229
  | { kind: 'transcript_activity'; newestActivityAt: number }
@@ -296,6 +308,7 @@ export const TURN_EVIDENCE_FIELD_SPECS: TurnEvidenceFieldSpecs = {
296
308
  strength: en(TURN_END_STRENGTHS), afterFinalizationTimeout: boolOpt, hollow: boolOpt,
297
309
  summary: { t: 'summary_ref', optional: true }, blockReason: enOpt(TURN_END_BLOCK_REASONS),
298
310
  releasedByHardCap: boolOpt, nativeOutcome: enOpt(NATIVE_TURN_OUTCOMES), live: { t: 'live', optional: true },
311
+ reportExpected: boolOpt,
299
312
  },
300
313
  transcript_final: {
301
314
  selfAttributing: bool, nativeRead: bool, nativeMarker: { t: 'native_marker', optional: true },
package/src/types.ts CHANGED
@@ -153,4 +153,12 @@ export interface RepoMeshSessionStatus {
153
153
  startedAt?: string | null
154
154
  lastActivityAt?: string | null
155
155
  isCached?: boolean
156
+ /**
157
+ * Worker MCP delivery outcome for this session's launch (present only for a
158
+ * mesh-delegated launch). `reason` is a short enum code, never free text —
159
+ * see `WORKER_MCP_DELIVERY_REASONS` (daemon-core's `mesh/worker-mcp-isolation.ts`,
160
+ * the source of the vocabulary this field's values are drawn from; not
161
+ * re-exported here so mesh-shared stays a dependency-free leaf).
162
+ */
163
+ workerMcp?: { delivered: boolean; reason?: string }
156
164
  }
@@ -31,6 +31,7 @@
31
31
  */
32
32
 
33
33
  import { renderMissionBriefBlock, type MissionBrief } from './mission-brief'
34
+ import { isMeshTaskDifficulty } from './brain-routing'
34
35
 
35
36
  export const WORKER_TOOLS = [
36
37
  'report_completion',
@@ -78,7 +79,23 @@ export function renderWorkerProtocolFooter(input: WorkerProtocolFooterInput = {}
78
79
  const scope: string[] = []
79
80
  if (input.taskId) scope.push(`task ${input.taskId}`)
80
81
  if (input.taskMode) scope.push(`mode ${input.taskMode}`)
81
- if (input.difficulty) scope.push(`difficulty ${input.difficulty}`)
82
+ // MCP-usage-audit item 2: validate against the canonical difficulty set
83
+ // (this package's own brain-routing vocabulary — never duplicated here)
84
+ // rather than rendering an internal caller's typo or stale value verbatim.
85
+ // The MCP tool-schema enum already blocks a bad value AT THE TOOLS; this
86
+ // covers internal callers that can still construct a task with one.
87
+ if (input.difficulty) {
88
+ if (isMeshTaskDifficulty(input.difficulty)) {
89
+ scope.push(`difficulty ${input.difficulty}`)
90
+ } else {
91
+ // eslint-disable-next-line no-console -- mesh-shared is a dependency-free
92
+ // leaf with no logger abstraction; this is the one place that matters.
93
+ console.warn(
94
+ `[worker-protocol] task ${input.taskId ?? '(unknown)'}: dropping invalid difficulty '${input.difficulty}' `
95
+ + `from the worker footer (expected one of easy, medium, difficult, freeform)`,
96
+ )
97
+ }
98
+ }
82
99
  if (input.readonly) scope.push('read-only')
83
100
  if (scope.length) lines.push(`You are a delegated worker (${scope.join(', ')}).`)
84
101
  else lines.push('You are a delegated worker.')
@@ -89,10 +106,15 @@ export function renderWorkerProtocolFooter(input: WorkerProtocolFooterInput = {}
89
106
  'For work that runs longer than a few minutes, call `progress_update` at natural checkpoints so the '
90
107
  + 'coordinator can see you are alive without polling.',
91
108
  '`peer_context_pull` shows what sibling tasks in this mission have reported; `git_status` / `git_diff` / '
92
- + '`git_log` inspect your own workspace (pass its absolute path as `workspace`).',
109
+ + '`git_log` inspect your own workspace — all three REQUIRE the absolute path to it as `workspace`.',
93
110
  'You have no coordinator tools (no `mesh_*`) by design: do not enqueue, dispatch, or restart anything. '
94
111
  + 'If you need a decision from the coordinator, finish with `report_completion` and outcome `blocked`, '
95
112
  + 'listing what you need in `blockers`.',
113
+ 'On a code-changing task, `report_completion` with outcome `completed` requires `touched_files` — send '
114
+ + '`[]` if you changed nothing, omitting the field is what gets refused. `blocked`/`failed` never need it, '
115
+ + 'and a read-only task should omit it or send `[]`.',
116
+ 'If `report_completion` is refused, the response carries `validationErrors` (or a `hint`) naming exactly '
117
+ + 'what to fix — correct that field and call it again; a refusal records nothing.',
96
118
  )
97
119
  if (input.enclosedHandoffNotes && input.enclosedHandoffNotes > 0) {
98
120
  lines.push(