@indigoai-us/hq-cli 5.345.56 → 5.345.58

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.
@@ -291,7 +291,7 @@ export function createLaneQuestionsHandlers(deps) {
291
291
  const body = parseOutpostQuestionBody(response.body, outpostId, question.question_id);
292
292
  return { question, audience, body };
293
293
  }
294
- async function loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, companyFilter) {
294
+ async function loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, companyFilter, questionId, visibilityFilter) {
295
295
  const hostedLanes = loadAllLanes(hqRoot).filter((lane) => lane.host.kind === "outpost" &&
296
296
  !laneIsHostedHere(ctx, lane) &&
297
297
  (!companyFilter ||
@@ -356,7 +356,7 @@ export function createLaneQuestionsHandlers(deps) {
356
356
  };
357
357
  }
358
358
  try {
359
- const remoteQuestions = await readOutpostQuestionRecords(ctx, outpostId, group.company);
359
+ const remoteQuestions = await readOutpostQuestionRecords(ctx, outpostId, group.company, questionId, visibilityFilter);
360
360
  const localLanesById = new Map(group.lanes.map((lane) => [lane.lane_id, lane]));
361
361
  const groupQuestions = [];
362
362
  const seenQuestionIds = new Set();
@@ -595,8 +595,32 @@ export function createLaneQuestionsHandlers(deps) {
595
595
  }
596
596
  return true;
597
597
  });
598
+ const deliveredLocalQuestions = new Map();
599
+ const currentSessionId = sessionIdFromEnv(ctx.env);
600
+ if (currentSessionId) {
601
+ const deliveredAt = ctx.now().toISOString();
602
+ for (const question of localQuestions) {
603
+ if (question.status !== "pending")
604
+ continue;
605
+ const audience = resolveAudience(hqRoot, question, ctx);
606
+ if (audience.kind !== "session" || audience.id !== currentSessionId)
607
+ continue;
608
+ const delivered = casUpdateQuestion(hqRoot, question.question_id, (current) => current.status !== "pending" ||
609
+ (current.seen_at && current.seen_session_id === currentSessionId)
610
+ ? current
611
+ : { ...current, seen_at: deliveredAt, seen_session_id: currentSessionId });
612
+ deliveredLocalQuestions.set(question.question_id, delivered);
613
+ }
614
+ }
598
615
  const outpostLoad = opts.allHosts
599
- ? await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, company)
616
+ ? await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, company, undefined, {
617
+ sessionId: currentSessionId ?? undefined,
618
+ for: opts.for,
619
+ from: opts.from,
620
+ project: opts.project,
621
+ story: opts.story,
622
+ status: opts.status,
623
+ })
600
624
  : { questions: [], errors: [] };
601
625
  for (const owner of outpostLoad.questions) {
602
626
  assertQuestionCompanyAccess(owner.question, owner.lane, company, hqRoot);
@@ -607,12 +631,13 @@ export function createLaneQuestionsHandlers(deps) {
607
631
  ...localQuestions
608
632
  .filter((q) => !remoteQuestionIds.has(q.question_id))
609
633
  .map((q) => {
634
+ const visibleQuestion = deliveredLocalQuestions.get(q.question_id) ?? q;
610
635
  const row = {
611
- ...q,
612
- priority: q.priority ?? null,
613
- priority_display: q.priority ?? "-",
614
- audience: resolveAudience(hqRoot, q, ctx),
615
- ...(q.question_kind === "continue" ? { flag: "continue-loop" } : {}),
636
+ ...visibleQuestion,
637
+ priority: visibleQuestion.priority ?? null,
638
+ priority_display: visibleQuestion.priority ?? "-",
639
+ audience: resolveAudience(hqRoot, visibleQuestion, ctx),
640
+ ...(visibleQuestion.question_kind === "continue" ? { flag: "continue-loop" } : {}),
616
641
  };
617
642
  if (!opts.allHosts)
618
643
  return row;
@@ -662,7 +687,7 @@ export function createLaneQuestionsHandlers(deps) {
662
687
  catch (err) {
663
688
  if (!isLanesError(err) || err.error !== "question_not_found")
664
689
  throw err;
665
- const lookup = await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot);
690
+ const lookup = await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, undefined, questionId);
666
691
  if (lookup.errors.length > 0) {
667
692
  throw new LanesError("outpost_question_lookup_unavailable", `Could not verify whether Outposts own question ${questionId}`, {
668
693
  question_id: questionId,
@@ -717,7 +742,7 @@ export function createLaneQuestionsHandlers(deps) {
717
742
  if (!isLanesError(err) || err.error !== "question_not_found")
718
743
  throw err;
719
744
  const company = explicitCompany ?? await resolveActiveQuestionCompany(ctx, "Cannot look up this lane question because the active HQ company could not be resolved; re-run with --company <slug>");
720
- const lookup = await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, company);
745
+ const lookup = await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, company, questionId);
721
746
  const matches = lookup.questions.filter((entry) => entry.question.question_id === questionId);
722
747
  if (matches.length === 0 && lookup.errors.length > 0) {
723
748
  throw new LanesError("outpost_question_lookup_unavailable", `Could not verify whether Outposts own question ${questionId}`, {
@@ -726,7 +751,7 @@ export function createLaneQuestionsHandlers(deps) {
726
751
  });
727
752
  }
728
753
  if (matches.length === 0) {
729
- const allCompanyLookup = await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot);
754
+ const allCompanyLookup = await loadPrimaryOwnedOutpostQuestions(ctx, hqRoot, undefined, questionId);
730
755
  const otherCompanyMatch = allCompanyLookup.questions.find((entry) => entry.question.question_id === questionId);
731
756
  if (otherCompanyMatch) {
732
757
  assertQuestionCompanyAccess(otherCompanyMatch.question, otherCompanyMatch.lane, company, hqRoot);
@@ -818,6 +843,7 @@ export function createLaneQuestionsHandlers(deps) {
818
843
  company: recordedCompany,
819
844
  body: nextBody,
820
845
  status: "answered",
846
+ delivered_at: null,
821
847
  answer: {
822
848
  choice,
823
849
  answer_kind: freeform ? "freeform" : "option",
@@ -91,6 +91,9 @@ export function createLaneResumeHandlers(deps) {
91
91
  const hqRoot = ctx.hqRoot();
92
92
  const resolution = await deps.resolveCommandLane(ctx, laneId);
93
93
  let lane = resolution.lane;
94
+ if (opts.freshThread === true && lane.loop_mode !== true) {
95
+ throw new LanesError("fresh_thread_loop_only", "--fresh-thread is reserved for loop-lane phases", { lane_id: laneId });
96
+ }
94
97
  const failedResumeReason = opts.reason?.trim();
95
98
  if (opts.reason !== undefined && opts.fromFailed !== true) {
96
99
  throw new LanesError("failed_resume_reason_requires_flag", "--reason on hq lanes resume requires --from-failed", { lane_id: laneId });
@@ -153,6 +156,9 @@ export function createLaneResumeHandlers(deps) {
153
156
  throw new LanesError("effort_resume_unsupported", `Provider ${lane.provider} cannot change effort on a resumed session`, { lane_id: laneId, provider: lane.provider });
154
157
  }
155
158
  lane = laneWithRecoveredProviderSession(hqRoot, lane);
159
+ if (opts.loopSessionId !== undefined && opts.loopSessionId !== lane.provider_session_id) {
160
+ lane = casUpdateLane(hqRoot, laneId, (current) => ({ ...current, provider_session_id: opts.loopSessionId }));
161
+ }
156
162
  if (opts.model !== undefined && opts.account === undefined) {
157
163
  throw new LanesError("model_requires_account", "--model on hq lanes resume is only accepted together with --account", {});
158
164
  }
@@ -233,7 +239,7 @@ export function createLaneResumeHandlers(deps) {
233
239
  }
234
240
  const refusal = queuedProfileRecovery || lane.state === "awaiting_input"
235
241
  ? null
236
- : resumeCommandRefusal(lane, { allowFailed: opts.fromFailed === true || isFailedQuotaKeepAliveLane(lane) });
242
+ : resumeCommandRefusal(lane, { allowFailed: opts.fromFailed === true || isFailedQuotaKeepAliveLane(lane), allowFresh: opts.freshThread === true });
237
243
  if (refusal !== null) {
238
244
  throw new LanesError(refusal.error, refusal.message, {
239
245
  lane_id: laneId,
@@ -435,6 +441,8 @@ export function createLaneResumeHandlers(deps) {
435
441
  ...(deliveryText !== undefined ? { text: deliveryText } : {}),
436
442
  ...(messageId !== undefined ? { message_id: messageId } : {}),
437
443
  compact: opts.compact !== false,
444
+ ...(opts.freshThread === true ? { fresh_thread: true } : {}),
445
+ ...(opts.workingDirectory ? { working_directory: opts.workingDirectory } : {}),
438
446
  },
439
447
  });
440
448
  lane = move.lane;
@@ -469,6 +477,8 @@ export function createLaneResumeHandlers(deps) {
469
477
  ...(deliveryText !== undefined ? { text: deliveryText } : {}),
470
478
  ...(messageId !== undefined ? { message_id: messageId } : {}),
471
479
  compact: opts.compact !== false,
480
+ ...(opts.freshThread === true ? { fresh_thread: true } : {}),
481
+ ...(opts.workingDirectory ? { working_directory: opts.workingDirectory } : {}),
472
482
  },
473
483
  accountSelection: selection,
474
484
  });
@@ -496,6 +506,8 @@ export function createLaneResumeHandlers(deps) {
496
506
  ...(deliveryText !== undefined ? { text: deliveryText } : {}),
497
507
  ...(messageId !== undefined ? { message_id: messageId } : {}),
498
508
  compact: opts.compact !== false,
509
+ ...(opts.freshThread === true ? { fresh_thread: true } : {}),
510
+ ...(opts.workingDirectory ? { working_directory: opts.workingDirectory } : {}),
499
511
  },
500
512
  });
501
513
  lane = move.lane;
@@ -535,6 +547,8 @@ export function createLaneResumeHandlers(deps) {
535
547
  ...(deliveryText !== undefined ? { text: deliveryText } : {}),
536
548
  ...(messageId !== undefined ? { message_id: messageId } : {}),
537
549
  compact: opts.compact !== false,
550
+ ...(opts.freshThread === true ? { fresh_thread: true } : {}),
551
+ ...(opts.workingDirectory ? { working_directory: opts.workingDirectory } : {}),
538
552
  at: quotaHoldAt,
539
553
  };
540
554
  resumed = markSpawnFailure(hqRoot, laneId, "account_quota_exhausted", {
@@ -590,6 +604,8 @@ export function createLaneResumeHandlers(deps) {
590
604
  ...(deliveryText !== undefined ? { text: deliveryText } : {}),
591
605
  ...(messageId !== undefined ? { message_id: messageId } : {}),
592
606
  compact: opts.compact !== false,
607
+ ...(opts.freshThread === true ? { fresh_thread: true } : {}),
608
+ ...(opts.workingDirectory ? { working_directory: opts.workingDirectory } : {}),
593
609
  at,
594
610
  },
595
611
  };
@@ -610,10 +626,12 @@ export function createLaneResumeHandlers(deps) {
610
626
  resolveMachineIdentity: ctx.resolveMachineIdentity,
611
627
  runCompaction: ctx.runCompaction,
612
628
  locateTranscript: ctx.locateTranscript,
629
+ ...(opts.workingDirectory ? { workingDirectory: opts.workingDirectory } : {}),
613
630
  }, {
614
631
  trigger: {
615
632
  kind: "resume",
616
633
  from: resumedFrom,
634
+ ...(opts.freshThread === true ? { fresh: true } : {}),
617
635
  ...(resumedFrom === "awaiting_input" && pendingQuestions.length === 0 ? { allowAwaitingInput: true } : {}),
618
636
  ...(opts.fromFailed === true ? { fromFailed: true } : {}),
619
637
  ...(failedResumeExpectedSenior !== undefined
@@ -77,6 +77,10 @@ export interface LaneResumeOptions {
77
77
  effort?: string;
78
78
  worker?: string;
79
79
  keepAlive?: boolean;
80
+ freshThread?: boolean;
81
+ workingDirectory?: string;
82
+ /** Internal loop runner session; selects this story's saved provider session. */
83
+ loopSessionId?: string;
80
84
  meshCompany?: string;
81
85
  profileRepinContext?: string;
82
86
  grantContext?: string;
@@ -458,7 +462,14 @@ export interface LaneQuestionsHandlerDeps {
458
462
  questionCompanyForLane(question: QuestionRecord, lane: Pick<LaneRecord, "company" | "mesh_company" | "project_id" | "lane_id">, hqRoot?: string): LaneCompanyRef | null;
459
463
  questionCompanyRefMatches(recordCompany: LaneCompanyRef | null | undefined, filter: LaneCompanyRef): boolean;
460
464
  questionListCompanyArg(company: LaneCompanyRef | null): string | undefined;
461
- readOutpostQuestionRecords(ctx: Ctx, outpostId: string, company: LaneCompanyRef | null): Promise<OutpostQuestionRow[]>;
465
+ readOutpostQuestionRecords(ctx: Ctx, outpostId: string, company: LaneCompanyRef | null, questionId?: string, visibilityFilter?: {
466
+ sessionId?: string;
467
+ for?: string;
468
+ from?: string;
469
+ project?: string;
470
+ story?: string;
471
+ status?: string;
472
+ }): Promise<OutpostQuestionRow[]>;
462
473
  readQuestionBody(hqRoot: string, question: QuestionRecord): QuestionBody | null;
463
474
  printJson(value: unknown): void;
464
475
  readSessionMeta(hqRoot: string, sessionId: string, ctx: Ctx): Record<string, unknown> | null;