@indigoai-us/hq-cli 5.299.0 → 5.301.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/assets/scaffold/core/scripts/token-usage-report.sh +9 -0
- package/dist/command-catalog.generated.d.ts +32 -6
- package/dist/command-catalog.generated.js +38 -6
- package/dist/commands/__fixtures__/access-vault.d.ts +2 -0
- package/dist/commands/__fixtures__/access-vault.js +1 -1
- package/dist/commands/access.d.ts +2 -1
- package/dist/commands/access.js +58 -49
- package/dist/commands/cloud-provision.js +51 -2
- package/dist/commands/cloud.js +12 -7
- package/dist/commands/core-company-index-scope-flag.d.ts +3 -0
- package/dist/commands/core-company-index-scope-flag.js +6 -0
- package/dist/commands/core-job-native-utils.d.ts +6 -0
- package/dist/commands/core-job-native-utils.js +80 -0
- package/dist/commands/core-job-native.d.ts +2 -4
- package/dist/commands/core-job-native.js +6 -10
- package/dist/commands/core-job-notify-native.d.ts +36 -0
- package/dist/commands/core-job-notify-native.js +359 -0
- package/dist/commands/core-job-remediate-native.d.ts +26 -0
- package/dist/commands/core-job-remediate-native.js +374 -0
- package/dist/commands/core.js +26 -8
- package/dist/commands/files.d.ts +1 -0
- package/dist/commands/files.js +90 -14
- package/dist/commands/integrations-api.d.ts +6 -6
- package/dist/commands/integrations-api.js +4 -13
- package/dist/commands/integrations-manage.js +1 -4
- package/dist/commands/lanes-message.js +78 -37
- package/dist/commands/lanes-resume.js +247 -210
- package/dist/commands/lanes-shared.d.ts +5 -1
- package/dist/commands/lanes.d.ts +4 -0
- package/dist/commands/lanes.js +7 -4
- package/dist/commands/meetings.js +35 -16
- package/dist/commands/safe-extract.d.ts +4 -4
- package/dist/commands/safe-extract.js +6 -7
- package/dist/lib/core-utils/token-usage-report.js +17 -3
- package/dist/lib/index-render/companies.d.ts +4 -1
- package/dist/lib/index-render/companies.js +66 -3
- package/dist/lib/index-render/index.d.ts +1 -0
- package/dist/lib/index-render/index.js +2 -1
- package/dist/lib/index-render/shared.d.ts +1 -1
- package/dist/lib/index-render/shared.js +4 -2
- package/dist/lib/lanes/reconcile.js +22 -4
- package/dist/lib/lanes/story-switch.d.ts +21 -0
- package/dist/lib/lanes/story-switch.js +75 -0
- package/dist/lib/lanes/types.d.ts +25 -0
- package/dist/lib/lanes/types.js +1 -1
- package/dist/lib/lanes/watcher.js +15 -4
- package/dist/lib/mesh/live/daemon/mqtt-events.d.ts +7 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.js +2 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/mqtt-events.test-d.js +10 -0
- package/dist/lib/mesh/live/daemon/presence.d.ts +2 -2
- package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +2 -2
- package/dist/utils/self-update.js +51 -4
- package/dist/utils/staged-npm-install.d.ts +16 -0
- package/dist/utils/staged-npm-install.js +264 -0
- package/dist/utils/update-command-supervisor.cjs +53 -1
- package/dist/utils/version-gate.d.ts +8 -0
- package/dist/utils/version-gate.js +82 -15
- package/package.json +2 -2
- package/dist/lib/install-path-compatibility-flag.d.ts +0 -9
- package/dist/lib/install-path-compatibility-flag.js +0 -12
|
@@ -5,6 +5,7 @@ import { parseCliEffort } from "../lib/lanes/effort.js";
|
|
|
5
5
|
import { parseOutpostHireGrantContext } from "../lib/lanes/hire-grant-dispatch.js";
|
|
6
6
|
import { assertNoSecretText } from "../lib/lanes/text-scan.js";
|
|
7
7
|
import { laneStories, laneStory, storyWarning } from "../lib/lanes/stories.js";
|
|
8
|
+
import { artifactIdentityKeys, rollbackStorySwitchAttribution, storySwitchNotice } from "../lib/lanes/story-switch.js";
|
|
8
9
|
import { spawnLaneWorker } from "../lib/lanes/spawn.js";
|
|
9
10
|
import { accountMoveSummary, accountRequestKeepsCurrentLane, assertCurrentResumeAccountAvailable, hasKeepAliveProgress, recordedResumeEffort, resetKeepAliveProgress, resolveResumeEffort } from "./lanes-shared.js";
|
|
10
11
|
export function createLaneResumeHandlers(deps) {
|
|
@@ -195,6 +196,8 @@ export function createLaneResumeHandlers(deps) {
|
|
|
195
196
|
}
|
|
196
197
|
const attributionChanged = projectId !== lane.project_id || storyId !== lane.story_id;
|
|
197
198
|
const messageId = text !== undefined || attributionChanged ? mintMessageId() : undefined;
|
|
199
|
+
const notice = storySwitchNotice(lane.project_id, lane.story_id, projectId, storyId);
|
|
200
|
+
const deliveryText = notice === undefined ? text : `${notice}${text === undefined ? "" : `\n\n${text}`}`;
|
|
198
201
|
let requestedMeshCompany;
|
|
199
202
|
let meshThreadId;
|
|
200
203
|
if (opts.meshCompany !== undefined) {
|
|
@@ -253,41 +256,60 @@ export function createLaneResumeHandlers(deps) {
|
|
|
253
256
|
account: keepCurrentAccount ? lane.account : opts.account ?? automaticAccountSelection?.selection.account,
|
|
254
257
|
model: opts.model,
|
|
255
258
|
});
|
|
256
|
-
let
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
...(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
259
|
+
let storySwitchRollback;
|
|
260
|
+
let storySwitchWorkerStarted = false;
|
|
261
|
+
if (messageId !== undefined && attributionChanged) {
|
|
262
|
+
lane = casUpdateLane(hqRoot, laneId, (current) => {
|
|
263
|
+
const already = current.attribution_history.some((entry) => entry.message_id === messageId);
|
|
264
|
+
if (already)
|
|
265
|
+
return current;
|
|
266
|
+
const existingStory = laneStory(current, projectId, storyId);
|
|
267
|
+
storySwitchRollback = {
|
|
268
|
+
message_id: messageId,
|
|
269
|
+
project_id: current.project_id,
|
|
270
|
+
story_id: current.story_id,
|
|
271
|
+
no_story_reason: current.no_story_reason,
|
|
272
|
+
...(!existingStory ? {
|
|
273
|
+
added_story: { project_id: projectId, story_id: storyId, status: "in_progress", added_at: at, updated_at: at },
|
|
274
|
+
} : {}),
|
|
275
|
+
story_switch_unacknowledged: current.story_switch_unacknowledged,
|
|
276
|
+
};
|
|
277
|
+
return {
|
|
278
|
+
...current,
|
|
279
|
+
project_id: projectId,
|
|
280
|
+
story_id: storyId,
|
|
281
|
+
no_story_reason: storyId ? undefined : current.no_story_reason,
|
|
282
|
+
stories: laneStory(current, projectId, storyId)
|
|
283
|
+
? laneStories(current)
|
|
284
|
+
: [
|
|
285
|
+
...laneStories(current),
|
|
286
|
+
{ project_id: projectId, story_id: storyId, status: "in_progress", added_at: at, updated_at: at },
|
|
287
|
+
],
|
|
288
|
+
attribution_history: appendCappedHistory(current.attribution_history, {
|
|
289
|
+
project_id: projectId,
|
|
290
|
+
story_id: storyId,
|
|
291
|
+
message_id: messageId,
|
|
292
|
+
at,
|
|
293
|
+
}),
|
|
294
|
+
story_switch_pending: {
|
|
295
|
+
previous_project_id: current.project_id,
|
|
296
|
+
previous_story_id: current.story_id,
|
|
297
|
+
project_id: projectId,
|
|
298
|
+
story_id: storyId,
|
|
299
|
+
message_id: messageId,
|
|
300
|
+
at,
|
|
301
|
+
previous_artifact_keys: artifactIdentityKeys(current.last_envelope?.artifacts),
|
|
302
|
+
},
|
|
303
|
+
story_switch_unacknowledged: undefined,
|
|
304
|
+
};
|
|
276
305
|
});
|
|
277
|
-
lane = move.lane;
|
|
278
306
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (
|
|
307
|
+
try {
|
|
308
|
+
let move = null;
|
|
309
|
+
if (opts.account !== undefined && !keepCurrentAccount) {
|
|
282
310
|
move = await prepareAccountMove(ctx, lane, {
|
|
283
|
-
account:
|
|
284
|
-
cliModel: undefined,
|
|
285
|
-
...(automaticAccountSelection.historyReason !== undefined
|
|
286
|
-
? { accountHistoryReason: automaticAccountSelection.historyReason }
|
|
287
|
-
: {}),
|
|
288
|
-
...(automaticAccountSelection.preservedModel !== undefined
|
|
289
|
-
? { preservedModel: automaticAccountSelection.preservedModel }
|
|
290
|
-
: {}),
|
|
311
|
+
account: opts.account,
|
|
312
|
+
cliModel: opts.model?.trim() || undefined,
|
|
291
313
|
...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
|
|
292
314
|
...(profileRepin !== undefined ? { profileRepin } : {}),
|
|
293
315
|
...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
@@ -299,204 +321,219 @@ export function createLaneResumeHandlers(deps) {
|
|
|
299
321
|
...(failedResumeExpectedSenior !== undefined
|
|
300
322
|
? { expected_senior: failedResumeExpectedSenior }
|
|
301
323
|
: {}),
|
|
302
|
-
...(
|
|
324
|
+
...(deliveryText !== undefined ? { text: deliveryText } : {}),
|
|
303
325
|
...(messageId !== undefined ? { message_id: messageId } : {}),
|
|
304
326
|
compact: opts.compact !== false,
|
|
305
327
|
},
|
|
306
|
-
accountSelection: selection,
|
|
307
328
|
});
|
|
308
329
|
lane = move.lane;
|
|
309
330
|
}
|
|
310
|
-
else {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
resumed = await withLaneAdmissionLock(hqRoot, lane.provider, async () => {
|
|
331
|
-
const admission = await assessLaneAdmission(ctx, lane, { queueAt: at });
|
|
332
|
-
if (admission.action === "refuse" && admission.error)
|
|
333
|
-
throw admission.error;
|
|
334
|
-
return withResumeHireAdmission(ctx, lane, () => {
|
|
335
|
-
if (admission.action === "queue") {
|
|
336
|
-
const sessionId = lane.provider_session_id;
|
|
337
|
-
if (!sessionId) {
|
|
338
|
-
throw new LanesError("no_provider_session", `Lane ${laneId} has no provider session id to queue this resume`, {
|
|
339
|
-
lane_id: laneId,
|
|
340
|
-
provider: lane.provider,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
queuedOnAdmission = true;
|
|
344
|
-
return casUpdateLane(hqRoot, laneId, (current) => {
|
|
345
|
-
if (current.version !== lane.version || current.state !== lane.state) {
|
|
346
|
-
throw new LanesError("lane_admission_conflict", `Lane ${laneId} changed while resume admission was checked`, {
|
|
347
|
-
lane_id: laneId,
|
|
348
|
-
prepared_version: lane.version,
|
|
349
|
-
version: current.version,
|
|
350
|
-
state: current.state,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
return {
|
|
354
|
-
...current,
|
|
355
|
-
state: "queued",
|
|
356
|
-
queued_reason: admission.reason ?? "throttle",
|
|
357
|
-
queued_details: undefined,
|
|
358
|
-
queued_at: at,
|
|
359
|
-
pending_resume: {
|
|
360
|
-
session_id: sessionId,
|
|
361
|
-
trigger: "resume",
|
|
362
|
-
caller: ctx.env.HQ_LANE_ID?.trim() || "user",
|
|
363
|
-
resumed_from: resumedFrom,
|
|
364
|
-
...(opts.fromFailed === true ? { from_failed: true } : {}),
|
|
365
|
-
...(failedResumeExpectedSenior !== undefined
|
|
366
|
-
? { expected_senior: failedResumeExpectedSenior }
|
|
367
|
-
: {}),
|
|
368
|
-
...(text !== undefined ? { text } : {}),
|
|
369
|
-
...(messageId !== undefined ? { message_id: messageId } : {}),
|
|
370
|
-
compact: opts.compact !== false,
|
|
371
|
-
at,
|
|
372
|
-
},
|
|
373
|
-
};
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
return spawnLaneWorker(hqRoot, lane, {
|
|
377
|
-
env: ctx.env,
|
|
378
|
-
modelDeps: {
|
|
379
|
-
readFileSync: ctx.readFileSync,
|
|
380
|
-
probe: ctx.probeProvider,
|
|
381
|
-
env: ctx.env,
|
|
382
|
-
},
|
|
383
|
-
existsSync: ctx.existsSync,
|
|
384
|
-
spawnProcess: ctx.spawnProcess,
|
|
385
|
-
proveDetached: ctx.proveDetached,
|
|
386
|
-
killProcess: ctx.killProcess,
|
|
387
|
-
now: ctx.now,
|
|
388
|
-
resolveMachineIdentity: ctx.resolveMachineIdentity,
|
|
389
|
-
runCompaction: ctx.runCompaction,
|
|
390
|
-
locateTranscript: ctx.locateTranscript,
|
|
391
|
-
}, {
|
|
392
|
-
trigger: {
|
|
393
|
-
kind: "resume",
|
|
394
|
-
from: resumedFrom,
|
|
395
|
-
...(opts.fromFailed === true ? { fromFailed: true } : {}),
|
|
331
|
+
else if (automaticAccountSelection !== undefined) {
|
|
332
|
+
const selection = automaticAccountSelection.selection;
|
|
333
|
+
if (selection.account !== lane.account) {
|
|
334
|
+
move = await prepareAccountMove(ctx, lane, {
|
|
335
|
+
account: selection.account,
|
|
336
|
+
cliModel: undefined,
|
|
337
|
+
...(automaticAccountSelection.historyReason !== undefined
|
|
338
|
+
? { accountHistoryReason: automaticAccountSelection.historyReason }
|
|
339
|
+
: {}),
|
|
340
|
+
...(automaticAccountSelection.preservedModel !== undefined
|
|
341
|
+
? { preservedModel: automaticAccountSelection.preservedModel }
|
|
342
|
+
: {}),
|
|
343
|
+
...(resumeProfile !== undefined ? { workerProfile: resumeProfile } : {}),
|
|
344
|
+
...(profileRepin !== undefined ? { profileRepin } : {}),
|
|
345
|
+
...(resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
346
|
+
...(hireGrantContext !== undefined ? { grantContext: hireGrantContext } : {}),
|
|
347
|
+
pending: {
|
|
348
|
+
trigger: "resume",
|
|
349
|
+
resumed_from: resumedFrom,
|
|
350
|
+
...(opts.fromFailed === true ? { from_failed: true } : {}),
|
|
396
351
|
...(failedResumeExpectedSenior !== undefined
|
|
397
|
-
? {
|
|
352
|
+
? { expected_senior: failedResumeExpectedSenior }
|
|
398
353
|
: {}),
|
|
399
|
-
...(
|
|
354
|
+
...(deliveryText !== undefined ? { text: deliveryText } : {}),
|
|
400
355
|
...(messageId !== undefined ? { message_id: messageId } : {}),
|
|
356
|
+
compact: opts.compact !== false,
|
|
401
357
|
},
|
|
402
|
-
|
|
403
|
-
effort: resumeEffort ?? recordedResumeEffort(lane),
|
|
358
|
+
accountSelection: selection,
|
|
404
359
|
});
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
360
|
+
lane = move.lane;
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
lane = persistClaudeAccountSelection(hqRoot, lane, selection);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
lane = recordResumeSetup(ctx, lane, {
|
|
367
|
+
...(move === null && resumeProfile !== undefined ? { profile: resumeProfile } : {}),
|
|
368
|
+
...(move === null && profileRepin !== undefined ? { profileRepin } : {}),
|
|
369
|
+
...(move === null && resumeEffort !== undefined ? { effort: resumeEffort } : {}),
|
|
370
|
+
...(keepAlive !== undefined ? { keepAlive } : {}),
|
|
371
|
+
...(move === null && keepCurrentAccount && opts.model !== undefined ? { cliModel: opts.model } : {}),
|
|
372
|
+
...(requestedMeshCompany !== undefined
|
|
373
|
+
? { meshCompany: requestedMeshCompany, threadId: meshThreadId }
|
|
374
|
+
: {}),
|
|
418
375
|
});
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
376
|
+
let queuedOnAdmission = false;
|
|
377
|
+
let resumed;
|
|
378
|
+
if (move?.queued) {
|
|
379
|
+
resumed = lane;
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
resumed = await withLaneAdmissionLock(hqRoot, lane.provider, async () => {
|
|
383
|
+
const admission = await assessLaneAdmission(ctx, lane, { queueAt: at });
|
|
384
|
+
if (admission.action === "refuse" && admission.error)
|
|
385
|
+
throw admission.error;
|
|
386
|
+
return withResumeHireAdmission(ctx, lane, () => {
|
|
387
|
+
if (admission.action === "queue") {
|
|
388
|
+
const sessionId = lane.provider_session_id;
|
|
389
|
+
if (!sessionId) {
|
|
390
|
+
throw new LanesError("no_provider_session", `Lane ${laneId} has no provider session id to queue this resume`, {
|
|
391
|
+
lane_id: laneId,
|
|
392
|
+
provider: lane.provider,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
queuedOnAdmission = true;
|
|
396
|
+
return casUpdateLane(hqRoot, laneId, (current) => {
|
|
397
|
+
if (current.version !== lane.version || current.state !== lane.state) {
|
|
398
|
+
throw new LanesError("lane_admission_conflict", `Lane ${laneId} changed while resume admission was checked`, {
|
|
399
|
+
lane_id: laneId,
|
|
400
|
+
prepared_version: lane.version,
|
|
401
|
+
version: current.version,
|
|
402
|
+
state: current.state,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
...current,
|
|
407
|
+
state: "queued",
|
|
408
|
+
queued_reason: admission.reason ?? "throttle",
|
|
409
|
+
queued_details: undefined,
|
|
410
|
+
queued_at: at,
|
|
411
|
+
pending_resume: {
|
|
412
|
+
session_id: sessionId,
|
|
413
|
+
trigger: "resume",
|
|
414
|
+
caller: ctx.env.HQ_LANE_ID?.trim() || "user",
|
|
415
|
+
resumed_from: resumedFrom,
|
|
416
|
+
...(opts.fromFailed === true ? { from_failed: true } : {}),
|
|
417
|
+
...(failedResumeExpectedSenior !== undefined
|
|
418
|
+
? { expected_senior: failedResumeExpectedSenior }
|
|
419
|
+
: {}),
|
|
420
|
+
...(deliveryText !== undefined ? { text: deliveryText } : {}),
|
|
421
|
+
...(messageId !== undefined ? { message_id: messageId } : {}),
|
|
422
|
+
compact: opts.compact !== false,
|
|
423
|
+
at,
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
const started = spawnLaneWorker(hqRoot, lane, {
|
|
429
|
+
env: ctx.env,
|
|
430
|
+
modelDeps: {
|
|
431
|
+
readFileSync: ctx.readFileSync,
|
|
432
|
+
probe: ctx.probeProvider,
|
|
433
|
+
env: ctx.env,
|
|
434
|
+
},
|
|
435
|
+
existsSync: ctx.existsSync,
|
|
436
|
+
spawnProcess: ctx.spawnProcess,
|
|
437
|
+
proveDetached: ctx.proveDetached,
|
|
438
|
+
killProcess: ctx.killProcess,
|
|
439
|
+
now: ctx.now,
|
|
440
|
+
resolveMachineIdentity: ctx.resolveMachineIdentity,
|
|
441
|
+
runCompaction: ctx.runCompaction,
|
|
442
|
+
locateTranscript: ctx.locateTranscript,
|
|
443
|
+
}, {
|
|
444
|
+
trigger: {
|
|
445
|
+
kind: "resume",
|
|
446
|
+
from: resumedFrom,
|
|
447
|
+
...(opts.fromFailed === true ? { fromFailed: true } : {}),
|
|
448
|
+
...(failedResumeExpectedSenior !== undefined
|
|
449
|
+
? { expectedSenior: failedResumeExpectedSenior }
|
|
450
|
+
: {}),
|
|
451
|
+
...(deliveryText !== undefined ? { text: deliveryText } : {}),
|
|
452
|
+
...(messageId !== undefined ? { message_id: messageId } : {}),
|
|
453
|
+
},
|
|
454
|
+
compact: opts.compact !== false,
|
|
455
|
+
effort: resumeEffort ?? recordedResumeEffort(lane),
|
|
456
|
+
});
|
|
457
|
+
storySwitchWorkerStarted = true;
|
|
458
|
+
deps.onLaneWorkerStarted?.(laneId, "resume");
|
|
459
|
+
return started;
|
|
460
|
+
}, hireGrantContext);
|
|
461
|
+
}, ctx.sleep);
|
|
462
|
+
}
|
|
463
|
+
if (deliveryText !== undefined && messageId !== undefined) {
|
|
464
|
+
// Same audit copy a message resume leaves; nothing drains it. Archive only
|
|
465
|
+
// after a successful spawn so a concurrent resume cannot leave a phantom.
|
|
466
|
+
archiveResumeMessage(hqRoot, laneId, {
|
|
427
467
|
at,
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
failure: recoveredFailure,
|
|
431
|
-
}),
|
|
432
|
-
}));
|
|
433
|
-
}
|
|
434
|
-
if (text !== undefined) {
|
|
435
|
-
// An operator text moves the compaction reference forward, as a message does.
|
|
436
|
-
next = casUpdateLane(hqRoot, laneId, (current) => ({
|
|
437
|
-
...resetKeepAliveProgress(current),
|
|
438
|
-
last_message_at: at,
|
|
439
|
-
}));
|
|
440
|
-
}
|
|
441
|
-
if (messageId !== undefined && attributionChanged) {
|
|
442
|
-
next = casUpdateLane(hqRoot, laneId, (current) => {
|
|
443
|
-
const already = current.attribution_history.some((entry) => entry.message_id === messageId);
|
|
444
|
-
if (already)
|
|
445
|
-
return current;
|
|
446
|
-
return {
|
|
447
|
-
...current,
|
|
468
|
+
message_id: messageId,
|
|
469
|
+
text: deliveryText,
|
|
448
470
|
project_id: projectId,
|
|
449
471
|
story_id: storyId,
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
updated_at: at,
|
|
461
|
-
},
|
|
462
|
-
],
|
|
463
|
-
attribution_history: appendCappedHistory(current.attribution_history, {
|
|
464
|
-
project_id: projectId,
|
|
465
|
-
story_id: storyId,
|
|
466
|
-
message_id: messageId,
|
|
472
|
+
resumed_from: resumedFrom,
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
let next = resumed;
|
|
476
|
+
if (failedResumeActor !== undefined &&
|
|
477
|
+
failedResumeReason !== undefined &&
|
|
478
|
+
recoveredFailure !== undefined) {
|
|
479
|
+
next = casUpdateLane(hqRoot, laneId, (current) => ({
|
|
480
|
+
...current,
|
|
481
|
+
failed_resume_history: appendCappedHistory(current.failed_resume_history ?? [], {
|
|
467
482
|
at,
|
|
483
|
+
actor: failedResumeActor,
|
|
484
|
+
reason: failedResumeReason,
|
|
485
|
+
failure: recoveredFailure,
|
|
468
486
|
}),
|
|
469
|
-
};
|
|
487
|
+
}));
|
|
488
|
+
}
|
|
489
|
+
if (deliveryText !== undefined) {
|
|
490
|
+
// An operator text moves the compaction reference forward, as a message does.
|
|
491
|
+
next = casUpdateLane(hqRoot, laneId, (current) => ({
|
|
492
|
+
...resetKeepAliveProgress(current),
|
|
493
|
+
last_message_at: at,
|
|
494
|
+
}));
|
|
495
|
+
}
|
|
496
|
+
if (text === undefined && hasKeepAliveProgress(next)) {
|
|
497
|
+
next = casUpdateLane(hqRoot, laneId, (current) => resetKeepAliveProgress(current));
|
|
498
|
+
}
|
|
499
|
+
printJson({
|
|
500
|
+
ok: true,
|
|
501
|
+
action: "resume",
|
|
502
|
+
lane_id: laneId,
|
|
503
|
+
resumed_from: resumedFrom,
|
|
504
|
+
message_id: messageId ?? null,
|
|
505
|
+
text: text ?? null,
|
|
506
|
+
delivery: "resume",
|
|
507
|
+
delivery_status: move?.queued
|
|
508
|
+
? "queued_on_target_account"
|
|
509
|
+
: queuedOnAdmission
|
|
510
|
+
? "queued_on_admission"
|
|
511
|
+
: "worker_spawned",
|
|
512
|
+
account_move: move ? accountMoveSummary(move) : null,
|
|
513
|
+
resumed_session_id: resumed.last_resume?.session_id ?? null,
|
|
514
|
+
last_resume: next.last_resume ?? null,
|
|
515
|
+
compaction: resumed.last_compaction && resumed.last_compaction.resume_at === resumed.last_resume?.at
|
|
516
|
+
? resumed.last_compaction
|
|
517
|
+
: null,
|
|
518
|
+
project_id: next.project_id,
|
|
519
|
+
story_id: next.story_id,
|
|
520
|
+
stories: laneStories(next),
|
|
521
|
+
story_warning: storyWarning(next) ?? null,
|
|
522
|
+
lane: next,
|
|
470
523
|
});
|
|
471
524
|
}
|
|
472
|
-
|
|
473
|
-
|
|
525
|
+
catch (err) {
|
|
526
|
+
if (storySwitchRollback !== undefined && !storySwitchWorkerStarted) {
|
|
527
|
+
try {
|
|
528
|
+
casUpdateLane(hqRoot, laneId, (current) => rollbackStorySwitchAttribution(current, storySwitchRollback));
|
|
529
|
+
}
|
|
530
|
+
catch (rollbackErr) {
|
|
531
|
+
const rollbackMessage = rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr);
|
|
532
|
+
process.stderr.write(`Could not roll back story attribution after resume failure: ${rollbackMessage}\n`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
throw err;
|
|
474
536
|
}
|
|
475
|
-
printJson({
|
|
476
|
-
ok: true,
|
|
477
|
-
action: "resume",
|
|
478
|
-
lane_id: laneId,
|
|
479
|
-
resumed_from: resumedFrom,
|
|
480
|
-
message_id: messageId ?? null,
|
|
481
|
-
text: text ?? null,
|
|
482
|
-
delivery: "resume",
|
|
483
|
-
delivery_status: move?.queued
|
|
484
|
-
? "queued_on_target_account"
|
|
485
|
-
: queuedOnAdmission
|
|
486
|
-
? "queued_on_admission"
|
|
487
|
-
: "worker_spawned",
|
|
488
|
-
account_move: move ? accountMoveSummary(move) : null,
|
|
489
|
-
resumed_session_id: resumed.last_resume?.session_id ?? null,
|
|
490
|
-
last_resume: next.last_resume ?? null,
|
|
491
|
-
compaction: resumed.last_compaction && resumed.last_compaction.resume_at === resumed.last_resume?.at
|
|
492
|
-
? resumed.last_compaction
|
|
493
|
-
: null,
|
|
494
|
-
project_id: next.project_id,
|
|
495
|
-
story_id: next.story_id,
|
|
496
|
-
stories: laneStories(next),
|
|
497
|
-
story_warning: storyWarning(next) ?? null,
|
|
498
|
-
lane: next,
|
|
499
|
-
});
|
|
500
537
|
}
|
|
501
538
|
return { runClaudeBalanceMoveTarget, runResume };
|
|
502
539
|
}
|
|
@@ -144,6 +144,10 @@ export interface OutpostLaneDispatchBehavior {
|
|
|
144
144
|
expectedOwnerHost?: LaneRecord["host"];
|
|
145
145
|
}
|
|
146
146
|
export interface LaneMessageHandlerDeps {
|
|
147
|
+
/** Test seam invoked after a provider worker has been started. */
|
|
148
|
+
onLaneWorkerStarted?: (laneId: string, command: "message" | "resume") => void;
|
|
149
|
+
/** Test seam invoked after a message has been durably queued for a lane. */
|
|
150
|
+
onLaneMessageQueued?: (laneId: string, path: "admission" | "account_move") => void;
|
|
147
151
|
archiveResumeMessage(hqRoot: string, laneId: string, message: {
|
|
148
152
|
message_id: string;
|
|
149
153
|
[key: string]: unknown;
|
|
@@ -184,7 +188,7 @@ export interface LaneMessageHandlerDeps {
|
|
|
184
188
|
validateResumeProfileModel(ctx: Ctx, lane: LaneRecord, profile: WorkerProfile): void;
|
|
185
189
|
withResumeHireAdmission<T>(ctx: Ctx, lane: LaneRecord, fn: () => T, grantContext?: OutpostHireGrantContext): Promise<T>;
|
|
186
190
|
}
|
|
187
|
-
export interface LaneResumeHandlerDeps extends Pick<LaneMessageHandlerDeps, "archiveResumeMessage" | "assertLaneRunsHereForAdmission" | "assertLaneRunsHereForOperatorMutation" | "assessLaneAdmission" | "casUpdateLane" | "laneIsHostedHere" | "laneWithRecoveredProviderSession" | "mintMessageId" | "prepareAccountMove" | "persistClaudeAccountSelection" | "printJson" | "recordResumeSetup" | "resolveCommandLane" | "resolveResumeProfile" | "selectAutomaticClaudeResumeAccount" | "validateResumeProfileModel" | "withResumeHireAdmission"> {
|
|
191
|
+
export interface LaneResumeHandlerDeps extends Pick<LaneMessageHandlerDeps, "onLaneWorkerStarted" | "archiveResumeMessage" | "assertLaneRunsHereForAdmission" | "assertLaneRunsHereForOperatorMutation" | "assessLaneAdmission" | "casUpdateLane" | "laneIsHostedHere" | "laneWithRecoveredProviderSession" | "mintMessageId" | "prepareAccountMove" | "persistClaudeAccountSelection" | "printJson" | "recordResumeSetup" | "resolveCommandLane" | "resolveResumeProfile" | "selectAutomaticClaudeResumeAccount" | "validateResumeProfileModel" | "withResumeHireAdmission"> {
|
|
188
192
|
dispatchOutpostLaneCommand(ctx: Ctx, lane: LaneRecord, action: "resume", options: LaneResumeOptions, behavior?: OutpostLaneDispatchBehavior): Promise<void>;
|
|
189
193
|
authorizeFailedResume(ctx: Ctx, lane: LaneRecord, encodedContext?: string): string;
|
|
190
194
|
authorizeResumeProfileRepin(ctx: Ctx, lane: LaneRecord, profile: ResolvedResumeProfile | undefined, encodedContext: string | undefined): ResumeProfileRepinAuthorization | undefined;
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ export declare const LANE_REMOTE_MIRROR_SYNC_FLAG = "lanes.remote-done-mirror";
|
|
|
29
29
|
/** The lane → mesh seam. Defined in lib/lanes/mesh-events.ts; the watcher shares it. */
|
|
30
30
|
export { emitLaneDecisionEvents };
|
|
31
31
|
export interface LanesCommandDeps {
|
|
32
|
+
/** Test seam for failures after a lane provider worker is started. */
|
|
33
|
+
onLaneWorkerStarted?: (laneId: string, command: "message" | "resume") => void;
|
|
34
|
+
/** Test seam for failures after a message is durably queued. */
|
|
35
|
+
onLaneMessageQueued?: (laneId: string, path: "admission" | "account_move") => void;
|
|
32
36
|
hqRoot?: () => string;
|
|
33
37
|
/** Start HQ-root walk here instead of process.cwd(). Tests only. */
|
|
34
38
|
cwd?: () => string;
|
package/dist/commands/lanes.js
CHANGED
|
@@ -7837,6 +7837,8 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
7837
7837
|
writeAnswerDeliveryFailed,
|
|
7838
7838
|
});
|
|
7839
7839
|
const messageHandlerDeps = {
|
|
7840
|
+
onLaneWorkerStarted: deps.onLaneWorkerStarted,
|
|
7841
|
+
onLaneMessageQueued: deps.onLaneMessageQueued,
|
|
7840
7842
|
archiveResumeMessage,
|
|
7841
7843
|
assertLaneRunsHereForAdmission,
|
|
7842
7844
|
assertLaneRunsHereForOperatorMutation,
|
|
@@ -7862,6 +7864,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
7862
7864
|
};
|
|
7863
7865
|
const messageHandlers = createLaneMessageHandlers(messageHandlerDeps);
|
|
7864
7866
|
const resumeHandlers = createLaneResumeHandlers({
|
|
7867
|
+
onLaneWorkerStarted: deps.onLaneWorkerStarted,
|
|
7865
7868
|
archiveResumeMessage,
|
|
7866
7869
|
assertLaneRunsHereForAdmission,
|
|
7867
7870
|
assertLaneRunsHereForOperatorMutation,
|
|
@@ -8214,8 +8217,8 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8214
8217
|
.description("Send a follow-up message to a lane")
|
|
8215
8218
|
.argument("<lane-id>", "Lane id")
|
|
8216
8219
|
.requiredOption("--text <msg>", "Message text")
|
|
8217
|
-
.option("--project <slug>", "Re-point project attribution
|
|
8218
|
-
.option("--story <id>", "Re-point story attribution
|
|
8220
|
+
.option("--project <slug>", "Re-point project attribution; a changed story is named in the resumed prompt")
|
|
8221
|
+
.option("--story <id>", "Re-point story attribution; a changed story is named in the resumed prompt")
|
|
8219
8222
|
.option("--account <id>", "Move the lane's session to this account of its provider before the message resumes it")
|
|
8220
8223
|
.option("--model <id>", "Model for the moved lane (with --account only)")
|
|
8221
8224
|
.option("--effort <level>", "Reasoning effort for the resumed provider turn")
|
|
@@ -8292,8 +8295,8 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
8292
8295
|
.option("--text <msg>", "Operator message for the resumed turn")
|
|
8293
8296
|
.option("--from-failed", "Explicitly resume a failed lane as its senior")
|
|
8294
8297
|
.option("--reason <text>", "Required explanation for resuming from failed")
|
|
8295
|
-
.option("--project <slug>", "Re-point project attribution
|
|
8296
|
-
.option("--story <id>", "Re-point story attribution
|
|
8298
|
+
.option("--project <slug>", "Re-point project attribution; a changed story is named in the resumed prompt")
|
|
8299
|
+
.option("--story <id>", "Re-point story attribution; a changed story is named in the resumed prompt")
|
|
8297
8300
|
.option("--account <id>", "Move the lane's session to this account of its provider before resuming")
|
|
8298
8301
|
.option("--model <id>", "Model for the moved lane (with --account only)")
|
|
8299
8302
|
.option("--effort <level>", "Reasoning effort for the resumed provider turn")
|