@indigoai-us/hq-cli 5.124.0 → 5.125.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 +31 -0
- package/dist/command-catalog.generated.d.ts +7 -1
- package/dist/command-catalog.generated.js +9 -1
- package/dist/commands/login.js +49 -6
- package/dist/commands/mesh.js +96 -19
- package/dist/lib/doctor/checks/work-context.js +4 -2
- package/dist/lib/mesh/live/backfill-held.d.ts +2 -0
- package/dist/lib/mesh/live/backfill-held.js +8 -4
- package/dist/lib/mesh/live/daemon/board-refresh.d.ts +3 -0
- package/dist/lib/mesh/live/daemon/board-refresh.js +6 -0
- package/dist/lib/mesh/live/daemon/doctor.d.ts +3 -0
- package/dist/lib/mesh/live/daemon/doctor.js +21 -1
- package/dist/lib/mesh/live/daemon/run.d.ts +11 -0
- package/dist/lib/mesh/live/daemon/run.js +129 -10
- package/dist/lib/mesh/live/daemon/state.d.ts +2 -0
- package/dist/lib/mesh/live/flush.d.ts +3 -0
- package/dist/lib/mesh/live/flush.js +11 -0
- package/dist/lib/work-context/company.d.ts +8 -0
- package/dist/lib/work-context/company.js +62 -18
- package/dist/lib/work-context/config.d.ts +35 -0
- package/dist/lib/work-context/config.js +307 -62
- package/dist/lib/work-context/contract.d.ts +2 -0
- package/dist/lib/work-context/default-company.d.ts +43 -0
- package/dist/lib/work-context/default-company.js +83 -0
- package/dist/lib/work-context/outbox.d.ts +5 -1
- package/dist/lib/work-context/outbox.js +5 -0
- package/dist/lib/work-context/reconcile.d.ts +4 -0
- package/dist/lib/work-context/reconcile.js +59 -3
- package/dist/lib/work-context/state.d.ts +2 -0
- package/dist/lib/work-context/state.js +14 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as fs from "node:fs";
|
|
6
6
|
import { InvalidSessionIdentityError, normalizeSessionIdentity, } from "../mesh/live/session-identity.js";
|
|
7
7
|
import { companyCorrectionPath, companySlugFromCwd, resolveCompany, } from "./company.js";
|
|
8
|
+
import { invalidateDefaultCompanyMemberships, } from "./config.js";
|
|
8
9
|
import { WORK_CONTEXT_CONTRACT_VERSION, normalizeTaskId, } from "./contract.js";
|
|
9
10
|
import { EXIT_INVALID_IDENTITY, EXIT_NOT_TRACKING, EXIT_OK, InvalidDecisionOriginError, NotTrackingError, } from "./errors.js";
|
|
10
11
|
import { decisionFromCandidates, } from "./organize.js";
|
|
@@ -24,6 +25,8 @@ function resultOf(parts) {
|
|
|
24
25
|
};
|
|
25
26
|
if (parts.companyUid)
|
|
26
27
|
out.companyUid = parts.companyUid;
|
|
28
|
+
if (parts.companySlug)
|
|
29
|
+
out.companySlug = parts.companySlug;
|
|
27
30
|
if (parts.projectId)
|
|
28
31
|
out.projectId = parts.projectId;
|
|
29
32
|
if (parts.taskId)
|
|
@@ -222,6 +225,7 @@ export async function reconcileObservation(obs, deps) {
|
|
|
222
225
|
hqRoot,
|
|
223
226
|
remoteOwnerSlug,
|
|
224
227
|
repoIdentityKey,
|
|
228
|
+
allowDeviceDefault: deps.allowDeviceDefault,
|
|
225
229
|
existingState: prior,
|
|
226
230
|
});
|
|
227
231
|
const nowIso = nowFn().toISOString();
|
|
@@ -341,8 +345,26 @@ export async function reconcileObservation(obs, deps) {
|
|
|
341
345
|
};
|
|
342
346
|
}
|
|
343
347
|
// Membership-validated slug→uid when local resolution only yielded a slug.
|
|
348
|
+
// A device default is weak, cached evidence: validate it authoritatively on
|
|
349
|
+
// every tenant-I/O path so a membership revocation takes effect immediately.
|
|
344
350
|
let resolvedCompany = company.company;
|
|
345
|
-
|
|
351
|
+
const deviceDefaultRequiresValidation = resolvedCompany.source === "device_default";
|
|
352
|
+
if (deviceDefaultRequiresValidation && !deps.validateMembership) {
|
|
353
|
+
return {
|
|
354
|
+
result: resultOf({
|
|
355
|
+
kind: "needs_company",
|
|
356
|
+
classification: "needs_company",
|
|
357
|
+
delivery: "clean",
|
|
358
|
+
sessionId,
|
|
359
|
+
clientOperationId: obs.clientOperationId,
|
|
360
|
+
}),
|
|
361
|
+
exitCode: EXIT_OK,
|
|
362
|
+
diagnostics: ["needs_company: device-default membership validator unavailable"],
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
if (resolvedCompany.slug &&
|
|
366
|
+
deps.validateMembership &&
|
|
367
|
+
(!resolvedCompany.uid || deviceDefaultRequiresValidation)) {
|
|
346
368
|
try {
|
|
347
369
|
const membership = await deps.validateMembership(resolvedCompany.slug);
|
|
348
370
|
if (membership && typeof membership === "object" && membership.uid) {
|
|
@@ -353,6 +375,9 @@ export async function reconcileObservation(obs, deps) {
|
|
|
353
375
|
diagnostics.push(`membership resolved companyUid for slug=${resolvedCompany.slug}`);
|
|
354
376
|
}
|
|
355
377
|
else if (membership === false) {
|
|
378
|
+
if (deviceDefaultRequiresValidation) {
|
|
379
|
+
invalidateDefaultCompanyMemberships({ root: deps.root, now: nowFn });
|
|
380
|
+
}
|
|
356
381
|
diagnostics.push(`membership denied for slug=${resolvedCompany.slug}; treating as needs_company`);
|
|
357
382
|
const statePath = writeSessionState(mergeLocalSessionFields({
|
|
358
383
|
contractVersion: WORK_CONTEXT_CONTRACT_VERSION,
|
|
@@ -384,6 +409,19 @@ export async function reconcileObservation(obs, deps) {
|
|
|
384
409
|
}
|
|
385
410
|
catch (err) {
|
|
386
411
|
diagnostics.push(`membership lookup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
412
|
+
if (deviceDefaultRequiresValidation) {
|
|
413
|
+
return {
|
|
414
|
+
result: resultOf({
|
|
415
|
+
kind: "needs_company",
|
|
416
|
+
classification: "needs_company",
|
|
417
|
+
delivery: "clean",
|
|
418
|
+
sessionId,
|
|
419
|
+
clientOperationId: obs.clientOperationId,
|
|
420
|
+
}),
|
|
421
|
+
exitCode: EXIT_OK,
|
|
422
|
+
diagnostics: [...diagnostics, "needs_company: device-default membership refresh unavailable"],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
387
425
|
}
|
|
388
426
|
}
|
|
389
427
|
// Company resolved — resolve project/task (US-007B order).
|
|
@@ -480,6 +518,11 @@ export async function reconcileObservation(obs, deps) {
|
|
|
480
518
|
registerClassification === "unresolved") {
|
|
481
519
|
registerClassification = "needs_project";
|
|
482
520
|
}
|
|
521
|
+
// Offline preflight has authoritative membership but deliberately does not
|
|
522
|
+
// invent a project. Expose that usable, validated state to hook consumers.
|
|
523
|
+
if (resolvedCompany.source === "device_default" && deps.offline && registerClassification === "unresolved") {
|
|
524
|
+
registerClassification = "needs_project";
|
|
525
|
+
}
|
|
483
526
|
// Do not let a weak later observation erase a server-acknowledged company
|
|
484
527
|
// binding. Keep the result unresolved so the normal candidate threshold can
|
|
485
528
|
// still advance the session, while preserving the durable emitter projection.
|
|
@@ -544,6 +587,7 @@ export async function reconcileObservation(obs, deps) {
|
|
|
544
587
|
sessionId,
|
|
545
588
|
clientOperationId: obs.clientOperationId,
|
|
546
589
|
companyUid: resolvedCompany.uid,
|
|
590
|
+
companySlug: resolvedCompany.slug,
|
|
547
591
|
projectId,
|
|
548
592
|
taskId,
|
|
549
593
|
bindingEpisodeId: acknowledgedPrior.bindingEpisodeId,
|
|
@@ -553,6 +597,9 @@ export async function reconcileObservation(obs, deps) {
|
|
|
553
597
|
};
|
|
554
598
|
}
|
|
555
599
|
let outboxOp;
|
|
600
|
+
const companyConfidence = company.status === "resolved" && company.company.source === "device_default"
|
|
601
|
+
? "device_default"
|
|
602
|
+
: undefined;
|
|
556
603
|
try {
|
|
557
604
|
outboxOp = enqueueOutbox({
|
|
558
605
|
clientOperationId: obs.clientOperationId,
|
|
@@ -562,6 +609,7 @@ export async function reconcileObservation(obs, deps) {
|
|
|
562
609
|
companySlug: resolvedCompany.slug,
|
|
563
610
|
projectId,
|
|
564
611
|
taskId,
|
|
612
|
+
companyConfidence,
|
|
565
613
|
kind: "register",
|
|
566
614
|
now: nowFn,
|
|
567
615
|
}, deps.root);
|
|
@@ -588,8 +636,9 @@ export async function reconcileObservation(obs, deps) {
|
|
|
588
636
|
diagnostics.push(`project source=${projectResolved.source} projectId=${projectId ?? "-"} taskId=${taskId ?? "-"}`);
|
|
589
637
|
}
|
|
590
638
|
let statePath;
|
|
639
|
+
let stateWrite;
|
|
591
640
|
try {
|
|
592
|
-
|
|
641
|
+
stateWrite = mergeLocalSessionFields({
|
|
593
642
|
contractVersion: WORK_CONTEXT_CONTRACT_VERSION,
|
|
594
643
|
sessionId,
|
|
595
644
|
companyUid: resolvedCompany.uid,
|
|
@@ -602,6 +651,7 @@ export async function reconcileObservation(obs, deps) {
|
|
|
602
651
|
...(typeof toolWrites === "number" ? { toolWrites } : {}),
|
|
603
652
|
...(hasTurn ? { hasTurn: true } : {}),
|
|
604
653
|
...(decision ? { decision } : {}),
|
|
654
|
+
...(companyConfidence ? { companyConfidence } : {}),
|
|
605
655
|
...transcriptFields,
|
|
606
656
|
}, prior);
|
|
607
657
|
statePath = writeSessionState(stateWrite, deps.root);
|
|
@@ -616,6 +666,7 @@ export async function reconcileObservation(obs, deps) {
|
|
|
616
666
|
sessionId,
|
|
617
667
|
clientOperationId: obs.clientOperationId,
|
|
618
668
|
companyUid: resolvedCompany.uid,
|
|
669
|
+
companySlug: resolvedCompany.slug,
|
|
619
670
|
projectId,
|
|
620
671
|
taskId,
|
|
621
672
|
errorCode: "NOT_TRACKING",
|
|
@@ -636,6 +687,7 @@ export async function reconcileObservation(obs, deps) {
|
|
|
636
687
|
sessionId,
|
|
637
688
|
clientOperationId: obs.clientOperationId,
|
|
638
689
|
companyUid: resolvedCompany.uid,
|
|
690
|
+
companySlug: resolvedCompany.slug,
|
|
639
691
|
projectId,
|
|
640
692
|
taskId,
|
|
641
693
|
}),
|
|
@@ -665,7 +717,10 @@ export async function reconcileObservation(obs, deps) {
|
|
|
665
717
|
...(typeof toolWrites === "number" ? { toolWrites } : {}),
|
|
666
718
|
...(hasTurn ? { hasTurn: true } : {}),
|
|
667
719
|
...transcriptFields,
|
|
668
|
-
},
|
|
720
|
+
},
|
|
721
|
+
// Merge against the just-written projection rather than the stale
|
|
722
|
+
// snapshot from before reconciliation (retains device confidence).
|
|
723
|
+
readSessionState(sessionId, deps.root) ?? stateWrite), deps.root);
|
|
669
724
|
}
|
|
670
725
|
const kind = registerClassification === "bound"
|
|
671
726
|
? "bound"
|
|
@@ -721,6 +776,7 @@ export async function reconcileObservation(obs, deps) {
|
|
|
721
776
|
sessionId,
|
|
722
777
|
clientOperationId: obs.clientOperationId,
|
|
723
778
|
companyUid: resolvedCompany.uid,
|
|
779
|
+
companySlug: resolvedCompany.slug,
|
|
724
780
|
projectId,
|
|
725
781
|
taskId,
|
|
726
782
|
errorCode: delivered.code,
|
|
@@ -89,6 +89,8 @@ export interface SessionStateFile {
|
|
|
89
89
|
* before SESSION_STATE sync (kept after AMEND-001).
|
|
90
90
|
*/
|
|
91
91
|
transcriptMarker?: "from transcript";
|
|
92
|
+
/** Historical repair under device-default evidence is intentionally weak. */
|
|
93
|
+
companyConfidence?: "device_default";
|
|
92
94
|
updatedAt: string;
|
|
93
95
|
}
|
|
94
96
|
/**
|
|
@@ -27,6 +27,7 @@ export const SESSION_STATE_ALLOWLIST = new Set([
|
|
|
27
27
|
"source",
|
|
28
28
|
"localHarness",
|
|
29
29
|
"transcriptMarker",
|
|
30
|
+
"companyConfidence",
|
|
30
31
|
"updatedAt",
|
|
31
32
|
]);
|
|
32
33
|
export function readSessionState(sessionId, root) {
|
|
@@ -100,6 +101,8 @@ export function projectSessionState(state) {
|
|
|
100
101
|
out.localHarness = state.localHarness;
|
|
101
102
|
if (state.transcriptMarker)
|
|
102
103
|
out.transcriptMarker = state.transcriptMarker;
|
|
104
|
+
if (state.companyConfidence)
|
|
105
|
+
out.companyConfidence = state.companyConfidence;
|
|
103
106
|
return out;
|
|
104
107
|
}
|
|
105
108
|
/**
|
|
@@ -148,6 +151,13 @@ export function mergeLocalSessionFields(next, prior, options = {}) {
|
|
|
148
151
|
if (!out.transcriptMarker && prior.transcriptMarker) {
|
|
149
152
|
out.transcriptMarker = prior.transcriptMarker;
|
|
150
153
|
}
|
|
154
|
+
const sameCompanyBinding = Boolean(prior.companyUid && out.companyUid && prior.companyUid === out.companyUid) ||
|
|
155
|
+
Boolean(prior.companySlug &&
|
|
156
|
+
out.companySlug &&
|
|
157
|
+
prior.companySlug.toLowerCase() === out.companySlug.toLowerCase());
|
|
158
|
+
if (!out.companyConfidence && prior.companyConfidence && sameCompanyBinding) {
|
|
159
|
+
out.companyConfidence = prior.companyConfidence;
|
|
160
|
+
}
|
|
151
161
|
const sameBinding = prior.companyUid === out.companyUid &&
|
|
152
162
|
prior.companySlug === out.companySlug &&
|
|
153
163
|
prior.projectId === out.projectId &&
|
|
@@ -237,6 +247,10 @@ export function isHookWrittenSessionState(state) {
|
|
|
237
247
|
export function authoritativeCompanyFromState(state) {
|
|
238
248
|
if (!state)
|
|
239
249
|
return null;
|
|
250
|
+
// A binding created only from the device default is deliberately weak. A
|
|
251
|
+
// later /startwork meta designation or remembered repo choice may replace it.
|
|
252
|
+
if (state.companyConfidence === "device_default")
|
|
253
|
+
return null;
|
|
240
254
|
const writable = new Set([
|
|
241
255
|
"unresolved",
|
|
242
256
|
"needs_project",
|