pi-crew 0.10.2 → 0.10.4
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/AGENTS.md +2 -1
- package/CHANGELOG.md +249 -0
- package/README.md +5 -1
- package/dist/index.mjs +10844 -7250
- package/docs/architecture.md +4 -4
- package/docs/commands-reference.md +3 -0
- package/docs/publishing.md +15 -3
- package/install.mjs +90 -39
- package/package.json +9 -3
- package/schema.json +11 -0
- package/scripts/README.md +4 -3
- package/skills/real-test-pi-crew/REPORT-TEMPLATE.md +7 -2
- package/skills/real-test-pi-crew/SKILL.md +428 -82
- package/src/config/config-merge.ts +11 -1
- package/src/config/config-validation.ts +40 -1
- package/src/config/config.ts +28 -6
- package/src/config/defaults.ts +35 -10
- package/src/config/env-vars.ts +27 -2
- package/src/config/migration-validator.ts +113 -0
- package/src/config/types.ts +36 -0
- package/src/extension/cross-extension-rpc.ts +3 -7
- package/src/extension/register.ts +13 -0
- package/src/extension/registration/lifecycle-handlers.ts +40 -9
- package/src/extension/registration/observability.ts +3 -7
- package/src/extension/registration/subagent-tools.ts +3 -7
- package/src/extension/registration/team-tool.ts +56 -12
- package/src/extension/registration/ui.ts +3 -8
- package/src/extension/registration/viewers.ts +3 -10
- package/src/extension/team-manager-command.ts +3 -7
- package/src/extension/team-tool/api/agent-control.ts +17 -10
- package/src/extension/team-tool/api/heartbeat.ts +4 -3
- package/src/extension/team-tool/api/mailbox.ts +33 -20
- package/src/extension/team-tool/api/plan-approval.ts +5 -5
- package/src/extension/team-tool/api/task-claims.ts +8 -7
- package/src/extension/team-tool/cancel.ts +6 -0
- package/src/extension/team-tool/doctor.ts +364 -7
- package/src/extension/team-tool/handle-settings.ts +23 -1
- package/src/extension/team-tool/inspect.ts +10 -2
- package/src/extension/team-tool/run.ts +3 -7
- package/src/extension/team-tool/status.ts +12 -0
- package/src/extension/team-tool.ts +41 -16
- package/src/hooks/registry.ts +62 -56
- package/src/prompt/inbox-poll.ts +90 -0
- package/src/prompt/message-tool.ts +166 -0
- package/src/prompt/prompt-runtime.ts +201 -18
- package/src/prompt/scratchpad-lifecycle.ts +3 -3
- package/src/prompt/surface-worker.ts +720 -0
- package/src/prompt/worker-events-channel.ts +49 -3
- package/src/runtime/async-runner.ts +29 -1
- package/src/runtime/background-runner.ts +43 -42
- package/src/runtime/broker/broker-issuer.ts +27 -2
- package/src/runtime/broker/crew-broker-tokens.ts +56 -4
- package/src/runtime/broker/crew-broker.ts +334 -443
- package/src/runtime/broker/delegate/delegate-event.ts +37 -0
- package/src/runtime/broker/mailbox-observer/mailbox-fanout.ts +59 -0
- package/src/runtime/broker/protocol/connection-state.ts +103 -0
- package/src/runtime/broker/protocol/events-replay.ts +68 -0
- package/src/runtime/broker/protocol/manifest-loader.ts +20 -0
- package/src/runtime/broker/protocol/msg-inbox.ts +69 -0
- package/src/runtime/broker/protocol/request-parsers.ts +175 -0
- package/src/runtime/broker/protocol/wait-auth.ts +46 -0
- package/src/runtime/child-pi/child-pi-spawn.ts +23 -9
- package/src/runtime/child-pi/child-pi-streams.ts +9 -1
- package/src/runtime/child-pi/child-pi.ts +368 -5
- package/src/runtime/crew-agent-records.ts +13 -1
- package/src/runtime/dispatch-batch.ts +12 -1
- package/src/runtime/event-log-tail-source.ts +374 -0
- package/src/runtime/finalize-run.ts +19 -7
- package/src/runtime/foreground-control.ts +19 -6
- package/src/runtime/goal-workflow/dynamic-workflow-context.ts +6 -0
- package/src/runtime/goal-workflow/dynamic-workflow-runner.ts +3 -0
- package/src/runtime/goal-workflow/goal-loop-runner.ts +29 -27
- package/src/runtime/goal-workflow/goal-state-store.ts +3 -0
- package/src/runtime/heartbeat/heartbeat-watcher.ts +3 -3
- package/src/runtime/live-session/live-agent-manager.ts +34 -1
- package/src/runtime/live-session/live-control-realtime.ts +10 -0
- package/src/runtime/live-session/live-session-runtime.ts +47 -27
- package/src/runtime/manifest-cache.ts +128 -17
- package/src/runtime/model/pi-args.ts +59 -65
- package/src/runtime/output/sidechain-output.ts +61 -6
- package/src/runtime/plan-replan.ts +3 -0
- package/src/runtime/process/proc-stat.ts +46 -0
- package/src/runtime/process/zombie-scanner.ts +32 -19
- package/src/runtime/spawn-policy.ts +27 -41
- package/src/runtime/stale-reconciler.ts +28 -3
- package/src/runtime/supervisor-contact.ts +3 -0
- package/src/runtime/surface/degrade.ts +776 -0
- package/src/runtime/surface/herdr-provider.ts +546 -0
- package/src/runtime/surface/launch-script.ts +172 -0
- package/src/runtime/surface/resolve-surface.ts +274 -0
- package/src/runtime/surface/surface-provider.ts +129 -0
- package/src/runtime/surface/surface-spawn.ts +475 -0
- package/src/runtime/surface/tmux-provider.ts +400 -0
- package/src/runtime/task-runner/child-executor.ts +80 -0
- package/src/runtime/task-runner/post-execution.ts +57 -2
- package/src/runtime/task-runner/prompt-builder.ts +1 -0
- package/src/runtime/task-runner/retrieval-orchestrator.ts +191 -56
- package/src/runtime/task-runner/state-helpers.ts +54 -30
- package/src/runtime/task-runner.ts +4 -2
- package/src/runtime/team-runner.ts +104 -3
- package/src/schema/config-schema.ts +24 -0
- package/src/state/atomic-write.ts +219 -40
- package/src/state/coordination/locks.ts +7 -5
- package/src/state/coordination/mailbox.ts +56 -10
- package/src/state/event-log/cursor.ts +413 -23
- package/src/state/event-log/event-log.ts +120 -113
- package/src/state/event-log/sequence-cache.ts +21 -3
- package/src/state/stores/ownership-map.ts +5 -4
- package/src/state/stores/plan-store.ts +12 -0
- package/src/state/stores/state-store.ts +103 -6
- package/src/state/types.ts +51 -0
- package/src/ui/inline-panel/agent-pane.ts +3 -0
- package/src/ui/powerbar-publisher.ts +3 -7
- package/src/ui/render-diff.ts +16 -8
- package/src/ui/run-action-dispatcher.ts +7 -10
- package/src/ui/run-dashboard.ts +87 -42
- package/src/ui/run-event-bus.ts +10 -1
- package/src/ui/run-snapshot-cache.ts +83 -35
- package/src/ui/settings-overlay.ts +4 -1
- package/src/ui/transcript-cache.ts +101 -13
- package/src/ui/transcript-viewer.ts +92 -24
- package/src/ui/widget/index.ts +32 -8
- package/src/utils/visual.ts +43 -0
- package/src/worktree/worktree-manager.ts +65 -4
|
@@ -18,17 +18,13 @@ import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
|
18
18
|
// Team tool handler — lazy-loaded because team-tool.ts imports many modules
|
|
19
19
|
import type { handleTeamTool as HandleTeamToolFn } from "../team-tool.ts";
|
|
20
20
|
|
|
21
|
-
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
22
21
|
async function handleTeamTool(
|
|
23
22
|
params: Parameters<typeof HandleTeamToolFn>[0],
|
|
24
23
|
ctx: Parameters<typeof HandleTeamToolFn>[1],
|
|
25
24
|
): Promise<ReturnType<typeof HandleTeamToolFn>> {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
30
|
-
}
|
|
31
|
-
return _cachedHandleTeamTool(params, ctx);
|
|
25
|
+
// LAZY: team-tool.ts imports many modules — defer until first use.
|
|
26
|
+
const mod = await import("../team-tool.ts");
|
|
27
|
+
return mod.handleTeamTool(params, ctx);
|
|
32
28
|
}
|
|
33
29
|
|
|
34
30
|
import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
@@ -147,6 +143,52 @@ export function detectUnrecognizedParams(schema: TObject, params: unknown): stri
|
|
|
147
143
|
].join("\n");
|
|
148
144
|
}
|
|
149
145
|
|
|
146
|
+
// PERF (2026-08-24): cheap read actions (status/summary/events/get/list) re-rendered
|
|
147
|
+
// the widget + powerbar after every call; the event bus and render tick already
|
|
148
|
+
// reflect run-state changes. Refresh only on actions that mutate run state
|
|
149
|
+
// outside the watched files. Names mirror the five domain dispatcher tables in
|
|
150
|
+
// src/schema/team-tool-schema.ts (RUN/STATUS/CONTROL/MANAGE/AUTOMATE). Actions
|
|
151
|
+
// whose effect depends on sub-params (api: approve-plan/agent-control/mailbox;
|
|
152
|
+
// plans: approve/reject; anchor/auto-summarize/auto_boomerang: set/clear/toggle;
|
|
153
|
+
// config/autonomy: show vs write) are included — a redundant refresh on their
|
|
154
|
+
// read sub-actions is cheap, a missed refresh after a write leaves stale UI.
|
|
155
|
+
const MUTATING_ACTIONS = new Set([
|
|
156
|
+
// run domain
|
|
157
|
+
"run",
|
|
158
|
+
"parallel",
|
|
159
|
+
"orchestrate",
|
|
160
|
+
"resume",
|
|
161
|
+
"retry",
|
|
162
|
+
"steer",
|
|
163
|
+
"goal",
|
|
164
|
+
"plans",
|
|
165
|
+
// control domain
|
|
166
|
+
"cancel",
|
|
167
|
+
"invalidate",
|
|
168
|
+
"respond",
|
|
169
|
+
"cleanup",
|
|
170
|
+
"prune",
|
|
171
|
+
"forget",
|
|
172
|
+
// manage domain
|
|
173
|
+
"create",
|
|
174
|
+
"update",
|
|
175
|
+
"delete",
|
|
176
|
+
"init",
|
|
177
|
+
"config",
|
|
178
|
+
"autonomy",
|
|
179
|
+
"settings",
|
|
180
|
+
"workflow-create",
|
|
181
|
+
"workflow-save",
|
|
182
|
+
"workflow-delete",
|
|
183
|
+
"import",
|
|
184
|
+
// automate domain
|
|
185
|
+
"schedule",
|
|
186
|
+
"anchor",
|
|
187
|
+
"auto-summarize",
|
|
188
|
+
"auto_boomerang",
|
|
189
|
+
"api",
|
|
190
|
+
]);
|
|
191
|
+
|
|
150
192
|
export function registerTeamTool(pi: ExtensionAPI, deps: RegisterTeamToolDeps): void {
|
|
151
193
|
const tool: ToolDefinition = {
|
|
152
194
|
name: "team",
|
|
@@ -229,11 +271,13 @@ export function registerTeamTool(pi: ExtensionAPI, deps: RegisterTeamToolDeps):
|
|
|
229
271
|
timestamp: Date.now(),
|
|
230
272
|
});
|
|
231
273
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
274
|
+
if (MUTATING_ACTIONS.has(resolved.action ?? "list")) {
|
|
275
|
+
const config = loadConfig(toolCtx.cwd).config.ui;
|
|
276
|
+
const cache = deps.getManifestCache(toolCtx.cwd);
|
|
277
|
+
const snapshotCache = deps.getRunSnapshotCache?.(toolCtx.cwd);
|
|
278
|
+
updateCrewWidget(toolCtx, deps.widgetState, config, cache, snapshotCache);
|
|
279
|
+
updatePiCrewPowerbar(pi.events, toolCtx.cwd, config, cache, snapshotCache, toolCtx);
|
|
280
|
+
}
|
|
237
281
|
return output;
|
|
238
282
|
} finally {
|
|
239
283
|
signal?.removeEventListener("abort", abort);
|
|
@@ -27,15 +27,10 @@ import type { CrewWidgetState } from "../../ui/widget/index.ts";
|
|
|
27
27
|
import { stopCrewWidget, updateCrewWidget } from "../../ui/widget/index.ts";
|
|
28
28
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
29
29
|
|
|
30
|
-
/** Cached live-run-sidebar constructor (lazy-loaded on first overlay open). */
|
|
31
|
-
let _cachedLiveRunSidebar: typeof LiveRunSidebarType | undefined;
|
|
32
30
|
async function importLiveRunSidebar(): Promise<typeof LiveRunSidebarType> {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
_cachedLiveRunSidebar = mod.LiveRunSidebar;
|
|
37
|
-
}
|
|
38
|
-
return _cachedLiveRunSidebar;
|
|
31
|
+
// LAZY: defer LiveRunSidebar import until the user opens a sidebar overlay.
|
|
32
|
+
const mod = await import("../../ui/live-run-sidebar.ts");
|
|
33
|
+
return mod.LiveRunSidebar;
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
/** Mutable state owned by register.ts. */
|
|
@@ -8,17 +8,10 @@ import { asCrewTheme } from "../../ui/theme-adapter.ts";
|
|
|
8
8
|
// Lazy-loaded: DurableTranscriptViewer is 658ms — only needed for /crew transcript command
|
|
9
9
|
import type { DurableTranscriptViewer as DurableTranscriptViewerType } from "../../ui/transcript-viewer.ts";
|
|
10
10
|
|
|
11
|
-
let _cachedViewer: typeof DurableTranscriptViewerType | undefined;
|
|
12
|
-
let _viewerPromise: Promise<typeof DurableTranscriptViewerType> | undefined;
|
|
13
11
|
async function getViewer(): Promise<typeof DurableTranscriptViewerType> {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
_cachedViewer = mod.DurableTranscriptViewer;
|
|
18
|
-
return mod.DurableTranscriptViewer;
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
return _viewerPromise;
|
|
12
|
+
// LAZY: DurableTranscriptViewer is 658ms — only needed for /crew transcript.
|
|
13
|
+
const mod = await import("../../ui/transcript-viewer.ts");
|
|
14
|
+
return mod.DurableTranscriptViewer;
|
|
22
15
|
}
|
|
23
16
|
|
|
24
17
|
export async function selectAgentTask(
|
|
@@ -3,17 +3,13 @@ import { listRuns } from "./run-index.ts";
|
|
|
3
3
|
// Lazy-loaded: team-tool.ts pulls in entire runtime chain.
|
|
4
4
|
import type { handleTeamTool as HandleTeamToolFn } from "./team-tool.ts";
|
|
5
5
|
|
|
6
|
-
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
7
6
|
async function handleTeamTool(
|
|
8
7
|
params: Parameters<typeof HandleTeamToolFn>[0],
|
|
9
8
|
ctx: Parameters<typeof HandleTeamToolFn>[1],
|
|
10
9
|
): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
15
|
-
}
|
|
16
|
-
return _cachedHandleTeamTool(params, ctx);
|
|
10
|
+
// LAZY: team-tool.ts pulls in the entire runtime chain.
|
|
11
|
+
const mod = await import("./team-tool.ts");
|
|
12
|
+
return mod.handleTeamTool(params, ctx);
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
import { isToolError, textFromToolResult } from "./tool-result.ts";
|
|
@@ -22,7 +22,8 @@ import {
|
|
|
22
22
|
appendSteeringMessage,
|
|
23
23
|
appendSteeringMessageAsync,
|
|
24
24
|
} from "../../../state/coordination/mailbox.ts";
|
|
25
|
-
import { appendEvent } from "../../../state/event-log/event-log.ts";
|
|
25
|
+
import { appendEvent, appendEventBuffered } from "../../../state/event-log/event-log.ts";
|
|
26
|
+
import { logInternalError } from "../../../utils/internal-error.ts";
|
|
26
27
|
import type { ApiOperationHandler } from "./handler-context.ts";
|
|
27
28
|
|
|
28
29
|
export const handleNudgeAgent: ApiOperationHandler = (hctx) => {
|
|
@@ -54,13 +55,19 @@ export const handleNudgeAgent: ApiOperationHandler = (hctx) => {
|
|
|
54
55
|
priority: "normal",
|
|
55
56
|
data: { source: "nudge-agent" },
|
|
56
57
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
// Read-your-writes (CI 2026-09-11, phase8 integration): the nudge caller
|
|
59
|
+
// reads events.jsonl synchronously right after dispatch — sync appendEvent.
|
|
60
|
+
try {
|
|
61
|
+
appendEvent(loaded.manifest.eventsPath, {
|
|
62
|
+
type: "agent.nudged",
|
|
63
|
+
runId: loaded.manifest.runId,
|
|
64
|
+
taskId: agent.taskId,
|
|
65
|
+
message: messageText,
|
|
66
|
+
data: { agentId: agent.id, mailboxMessageId: message.id },
|
|
67
|
+
});
|
|
68
|
+
} catch (e) {
|
|
69
|
+
logInternalError("api.agent-control.append", e, "type=agent.nudged");
|
|
70
|
+
}
|
|
64
71
|
ctx.events?.emit?.("crew.mailbox.message", {
|
|
65
72
|
runId: loaded.manifest.runId,
|
|
66
73
|
id: message.id,
|
|
@@ -314,7 +321,7 @@ export const handleLiveAgentControl: ApiOperationHandler = async (hctx) => {
|
|
|
314
321
|
: undefined;
|
|
315
322
|
publishLiveControlRealtime(request);
|
|
316
323
|
ctx.events?.emit?.("pi-crew:live-control", liveControlRealtimeMessage(request));
|
|
317
|
-
|
|
324
|
+
appendEventBuffered(loaded.manifest.eventsPath, {
|
|
318
325
|
type: "agent.control.queued",
|
|
319
326
|
runId: loaded.manifest.runId,
|
|
320
327
|
taskId: agent.taskId,
|
|
@@ -324,7 +331,7 @@ export const handleLiveAgentControl: ApiOperationHandler = async (hctx) => {
|
|
|
324
331
|
mailboxMessageId: mailboxMessage?.id,
|
|
325
332
|
realtime: true,
|
|
326
333
|
},
|
|
327
|
-
});
|
|
334
|
+
}).catch((e) => logInternalError("api.agent-control.buffered", e, "type=agent.control.queued"));
|
|
328
335
|
return result(JSON.stringify({ queued: true, request, mailboxMessage }, null, 2), {
|
|
329
336
|
action: "api",
|
|
330
337
|
status: "ok",
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
import { touchWorkerHeartbeat } from "../../../runtime/heartbeat/worker-heartbeat.ts";
|
|
9
9
|
import { isTerminalTaskStatus } from "../../../state/contracts.ts";
|
|
10
10
|
import { withRunLockSync } from "../../../state/coordination/locks.ts";
|
|
11
|
-
import {
|
|
11
|
+
import { appendEventBuffered } from "../../../state/event-log/event-log.ts";
|
|
12
12
|
import { loadRunManifestById, saveRunTasks } from "../../../state/stores/state-store.ts";
|
|
13
|
+
import { logInternalError } from "../../../utils/internal-error.ts";
|
|
13
14
|
import { RUN_NOT_FOUND_HINT } from "../run-not-found.ts";
|
|
14
15
|
import type { ApiOperationHandler } from "./handler-context.ts";
|
|
15
16
|
|
|
@@ -79,12 +80,12 @@ export const handleWriteHeartbeat: ApiOperationHandler = (hctx) => {
|
|
|
79
80
|
);
|
|
80
81
|
const tasks = fresh.tasks.map((item) => (item.id === freshTask.id ? { ...item, heartbeat } : item));
|
|
81
82
|
saveRunTasks(fresh.manifest, tasks);
|
|
82
|
-
|
|
83
|
+
appendEventBuffered(fresh.manifest.eventsPath, {
|
|
83
84
|
type: "worker.heartbeat",
|
|
84
85
|
runId: fresh.manifest.runId,
|
|
85
86
|
taskId: freshTask.id,
|
|
86
87
|
data: { ...heartbeat },
|
|
87
|
-
});
|
|
88
|
+
}).catch((e) => logInternalError("api.heartbeat.buffered", e, "type=worker.heartbeat"));
|
|
88
89
|
return result(JSON.stringify(heartbeat, null, 2), {
|
|
89
90
|
action: "api",
|
|
90
91
|
status: "ok",
|
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
readMailboxMessage,
|
|
17
17
|
validateMailbox,
|
|
18
18
|
} from "../../../state/coordination/mailbox.ts";
|
|
19
|
-
import { appendEvent } from "../../../state/event-log/event-log.ts";
|
|
19
|
+
import { appendEvent, appendEventBuffered } from "../../../state/event-log/event-log.ts";
|
|
20
|
+
import { logInternalError } from "../../../utils/internal-error.ts";
|
|
20
21
|
import type { ApiOperationHandler } from "./handler-context.ts";
|
|
21
22
|
|
|
22
23
|
export const handleReadMailbox: ApiOperationHandler = (hctx) => {
|
|
@@ -129,11 +130,11 @@ export const handleSendMessage: ApiOperationHandler = (hctx) => {
|
|
|
129
130
|
// run-lock callback. Sync append (byte-identical to pre-extract
|
|
130
131
|
// api.ts) so consumers reading eventsPath immediately after the
|
|
131
132
|
// call see the event — async fire-and-forget would race.
|
|
132
|
-
|
|
133
|
+
appendEventBuffered(loaded.manifest.eventsPath, {
|
|
133
134
|
type: "mailbox.message",
|
|
134
135
|
runId: loaded.manifest.runId,
|
|
135
136
|
data: { id: message.id, direction, from, to },
|
|
136
|
-
});
|
|
137
|
+
}).catch((e) => logInternalError("api.mailbox.buffered", e, "type=mailbox.message"));
|
|
137
138
|
ctx.events?.emit?.("crew.mailbox.message", {
|
|
138
139
|
runId: loaded.manifest.runId,
|
|
139
140
|
id: message.id,
|
|
@@ -185,26 +186,38 @@ export const handleAckMessage: ApiOperationHandler = (hctx) => {
|
|
|
185
186
|
return withRunLockSync(loaded.manifest, () => {
|
|
186
187
|
const message = readMailboxMessage(loaded.manifest, messageId);
|
|
187
188
|
const delivery = acknowledgeMailboxMessage(loaded.manifest, messageId);
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (message?.data?.kind === "group_join" && typeof message.data.requestId === "string") {
|
|
189
|
+
// Read-your-writes (CI 2026-09-11, phase4 integration): the ack API
|
|
190
|
+
// returns and callers/tests read events.jsonl synchronously right after —
|
|
191
|
+
// buffered append flushes later (bufferMs window), so the events were
|
|
192
|
+
// missing at read. Sync appendEvent (base behavior, b6eba80f pattern).
|
|
193
|
+
try {
|
|
194
194
|
appendEvent(loaded.manifest.eventsPath, {
|
|
195
|
-
type: "
|
|
195
|
+
type: "mailbox.acknowledged",
|
|
196
196
|
runId: loaded.manifest.runId,
|
|
197
|
-
|
|
198
|
-
data: {
|
|
199
|
-
requestId: message.data.requestId,
|
|
200
|
-
messageId,
|
|
201
|
-
batchId: message.data.batchId,
|
|
202
|
-
partial: message.data.partial,
|
|
203
|
-
acknowledgedAt: delivery.updatedAt,
|
|
204
|
-
acknowledgedBy: "leader",
|
|
205
|
-
},
|
|
206
|
-
metadata: { provenance: "api" },
|
|
197
|
+
data: { messageId },
|
|
207
198
|
});
|
|
199
|
+
} catch (e) {
|
|
200
|
+
logInternalError("api.mailbox.append", e, "type=mailbox.acknowledged");
|
|
201
|
+
}
|
|
202
|
+
if (message?.data?.kind === "group_join" && typeof message.data.requestId === "string") {
|
|
203
|
+
try {
|
|
204
|
+
appendEvent(loaded.manifest.eventsPath, {
|
|
205
|
+
type: "agent.group_join.acknowledged",
|
|
206
|
+
runId: loaded.manifest.runId,
|
|
207
|
+
message: "Group join delivery acknowledged via mailbox ack.",
|
|
208
|
+
data: {
|
|
209
|
+
requestId: message.data.requestId,
|
|
210
|
+
messageId,
|
|
211
|
+
batchId: message.data.batchId,
|
|
212
|
+
partial: message.data.partial,
|
|
213
|
+
acknowledgedAt: delivery.updatedAt,
|
|
214
|
+
acknowledgedBy: "leader",
|
|
215
|
+
},
|
|
216
|
+
metadata: { provenance: "api" },
|
|
217
|
+
});
|
|
218
|
+
} catch (e) {
|
|
219
|
+
logInternalError("api.mailbox.append", e, "type=agent.group_join.acknowledged");
|
|
220
|
+
}
|
|
208
221
|
}
|
|
209
222
|
ctx.events?.emit?.("crew.mailbox.acknowledged", {
|
|
210
223
|
runId: loaded.manifest.runId,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { terminateLiveAgentsForRun } from "../../../runtime/live-session/live-agent-manager.ts";
|
|
9
9
|
import { currentCrewRole, permissionForRole } from "../../../runtime/role-permission.ts";
|
|
10
10
|
import { withRunLock } from "../../../state/coordination/locks.ts";
|
|
11
|
-
import { appendEvent } from "../../../state/event-log/event-log.ts";
|
|
11
|
+
import { appendEvent, appendEventBuffered } from "../../../state/event-log/event-log.ts";
|
|
12
12
|
import { getCurrentPlanRecord, setPlanApproval } from "../../../state/stores/plan-store.ts";
|
|
13
13
|
import { loadRunManifestById, saveRunManifestAsync, saveRunTasks, updateRunStatus } from "../../../state/stores/state-store.ts";
|
|
14
14
|
import { logInternalError } from "../../../utils/internal-error.ts";
|
|
@@ -68,13 +68,13 @@ export const handleApprovePlan: ApiOperationHandler = async (hctx) => {
|
|
|
68
68
|
// plan id+version. Pre-v2 runs without a PlanRecord skip silently.
|
|
69
69
|
const currentRecord = getCurrentPlanRecord(manifest);
|
|
70
70
|
if (currentRecord) setPlanApproval(manifest, { status: "approved", planVersion: currentRecord.version, by: "api" });
|
|
71
|
-
|
|
71
|
+
appendEventBuffered(manifest.eventsPath, {
|
|
72
72
|
type: "plan.approved",
|
|
73
73
|
runId: manifest.runId,
|
|
74
74
|
taskId: approval.planTaskId,
|
|
75
75
|
message: "Adaptive implementation plan approved; resume the run to execute mutating tasks.",
|
|
76
76
|
metadata: { provenance: "api" },
|
|
77
|
-
});
|
|
77
|
+
}).catch((e) => logInternalError("api.plan-approval.buffered", e, "type=plan.approved"));
|
|
78
78
|
return result(JSON.stringify(manifest.planApproval, null, 2), {
|
|
79
79
|
action: "api",
|
|
80
80
|
status: "ok",
|
|
@@ -150,13 +150,13 @@ export const handleCancelPlan: ApiOperationHandler = async (hctx) => {
|
|
|
150
150
|
const denyRecord = getCurrentPlanRecord(manifest);
|
|
151
151
|
if (denyRecord) setPlanApproval(manifest, { status: "rejected", planVersion: denyRecord.version, by: "api" });
|
|
152
152
|
saveRunTasks(manifest, tasks);
|
|
153
|
-
|
|
153
|
+
appendEventBuffered(manifest.eventsPath, {
|
|
154
154
|
type: "plan.cancelled",
|
|
155
155
|
runId: manifest.runId,
|
|
156
156
|
taskId: approval.planTaskId,
|
|
157
157
|
message: "Adaptive implementation plan was cancelled.",
|
|
158
158
|
metadata: { provenance: "api" },
|
|
159
|
-
});
|
|
159
|
+
}).catch((e) => logInternalError("api.plan-approval.buffered", e, "type=plan.cancelled"));
|
|
160
160
|
manifest = updateRunStatus(manifest, "cancelled", "Plan approval was cancelled.");
|
|
161
161
|
void terminateLiveAgentsForRun(manifest.runId, "cancelled", appendEvent, manifest.eventsPath).catch((error) =>
|
|
162
162
|
logInternalError("team-tool.cancel-plan.terminate", error, `runId=${manifest.runId}`),
|
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
import { canTransitionTaskStatus, isTeamTaskStatus } from "../../../state/contracts.ts";
|
|
17
17
|
import { withRunLockSync } from "../../../state/coordination/locks.ts";
|
|
18
18
|
import { claimTask, releaseTaskClaim, transitionClaimedTaskStatus } from "../../../state/coordination/task-claims.ts";
|
|
19
|
-
import {
|
|
19
|
+
import { appendEventBuffered } from "../../../state/event-log/event-log.ts";
|
|
20
20
|
import { loadRunManifestById, saveRunTasks } from "../../../state/stores/state-store.ts";
|
|
21
21
|
import type { TeamTaskState } from "../../../state/types.ts";
|
|
22
|
+
import { logInternalError } from "../../../utils/internal-error.ts";
|
|
22
23
|
import { RUN_NOT_FOUND_HINT } from "../run-not-found.ts";
|
|
23
24
|
import type { ApiHandlerContext, ApiOperationHandler } from "./handler-context.ts";
|
|
24
25
|
|
|
@@ -92,7 +93,7 @@ export const handleClaimTask: ApiOperationHandler = (ctx) => {
|
|
|
92
93
|
const updatedTask = claimTask(freshTask, owner);
|
|
93
94
|
const tasks = fresh.tasks.map((item) => (item.id === freshTask.id ? updatedTask : item));
|
|
94
95
|
saveRunTasks(fresh.manifest, tasks);
|
|
95
|
-
|
|
96
|
+
appendEventBuffered(fresh.manifest.eventsPath, {
|
|
96
97
|
type: "task.claimed",
|
|
97
98
|
runId: fresh.manifest.runId,
|
|
98
99
|
taskId: freshTask.id,
|
|
@@ -101,7 +102,7 @@ export const handleClaimTask: ApiOperationHandler = (ctx) => {
|
|
|
101
102
|
token: "[REDACTED]",
|
|
102
103
|
leasedUntil: updatedTask.claim?.leasedUntil,
|
|
103
104
|
},
|
|
104
|
-
});
|
|
105
|
+
}).catch((e) => logInternalError("api.task-claims.buffered", e, "type=task.claimed"));
|
|
105
106
|
return result(JSON.stringify(updatedTask.claim, null, 2), {
|
|
106
107
|
action: "api",
|
|
107
108
|
status: "ok",
|
|
@@ -162,12 +163,12 @@ export const handleReleaseTaskClaim: ApiOperationHandler = (ctx) => {
|
|
|
162
163
|
const updatedTask = releaseTaskClaim(freshTask, owner, token);
|
|
163
164
|
const tasks = fresh.tasks.map((item) => (item.id === freshTask.id ? updatedTask : item));
|
|
164
165
|
saveRunTasks(fresh.manifest, tasks);
|
|
165
|
-
|
|
166
|
+
appendEventBuffered(fresh.manifest.eventsPath, {
|
|
166
167
|
type: "task.claim_released",
|
|
167
168
|
runId: fresh.manifest.runId,
|
|
168
169
|
taskId: freshTask.id,
|
|
169
170
|
data: { owner },
|
|
170
|
-
});
|
|
171
|
+
}).catch((e) => logInternalError("api.task-claims.buffered", e, "type=task.claim_released"));
|
|
171
172
|
return result(JSON.stringify(updatedTask, null, 2), {
|
|
172
173
|
action: "api",
|
|
173
174
|
status: "ok",
|
|
@@ -241,12 +242,12 @@ export const handleTransitionTaskStatus: ApiOperationHandler = (ctx) => {
|
|
|
241
242
|
const updatedTask = transitionClaimedTaskStatus(freshTask, owner, token, to);
|
|
242
243
|
const tasks = fresh.tasks.map((item) => (item.id === freshTask.id ? updatedTask : item));
|
|
243
244
|
saveRunTasks(fresh.manifest, tasks);
|
|
244
|
-
|
|
245
|
+
appendEventBuffered(fresh.manifest.eventsPath, {
|
|
245
246
|
type: "task.status_transitioned",
|
|
246
247
|
runId: fresh.manifest.runId,
|
|
247
248
|
taskId: freshTask.id,
|
|
248
249
|
data: { owner, status: to },
|
|
249
|
-
});
|
|
250
|
+
}).catch((e) => logInternalError("api.task-claims.buffered", e, "type=task.status_transitioned"));
|
|
250
251
|
return result(JSON.stringify(updatedTask, null, 2), {
|
|
251
252
|
action: "api",
|
|
252
253
|
status: "ok",
|
|
@@ -392,6 +392,12 @@ export async function handleCancel(params: TeamToolParamsValue, ctx: TeamContext
|
|
|
392
392
|
}
|
|
393
393
|
ctx.abortForegroundRun?.(fresh.manifest.runId);
|
|
394
394
|
for (const taskId of abortResult.abortedIds) {
|
|
395
|
+
// REVIEW FIX (2026-09-10): reverted M2b buffered conversion. Although
|
|
396
|
+
// task.cancelled is a TERMINAL type (buffered terminal path bypasses
|
|
397
|
+
// the 20ms buffer), that path still resolves via a microtask chain
|
|
398
|
+
// (flushPromise.then(appendEvent)) — NOT same-tick. Cancel tests and
|
|
399
|
+
// the immediately-following updateRunStatus reader need same-tick
|
|
400
|
+
// durability, so plain sync appendEvent (base behavior).
|
|
395
401
|
appendEvent(fresh.manifest.eventsPath, {
|
|
396
402
|
type: "task.cancelled",
|
|
397
403
|
runId: fresh.manifest.runId,
|