@indigoai-us/hq-cli 5.140.0 → 5.142.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 +25 -0
- package/assets/scaffold/core/scripts/checkpoint-stop-gate.sh +77 -2
- package/dist/command-catalog.generated.d.ts +6 -0
- package/dist/command-catalog.generated.js +8 -0
- package/dist/commands/bot-jobs.js +16 -3
- package/dist/commands/lanes.js +125 -26
- package/dist/lib/agent-kit/run/heartbeat.js +2 -2
- package/dist/lib/bot/run.js +2 -2
- package/dist/lib/heartbeat-identity.d.ts +1 -0
- package/dist/lib/heartbeat-identity.js +5 -0
- package/dist/lib/lanes/effort.d.ts +3 -2
- package/dist/lib/lanes/effort.js +5 -1
- package/dist/lib/lanes/errors.d.ts +1 -1
- package/dist/lib/lanes/index.d.ts +2 -0
- package/dist/lib/lanes/index.js +1 -0
- package/dist/lib/lanes/mesh-events.d.ts +4 -5
- package/dist/lib/lanes/mesh-events.js +14 -4
- package/dist/lib/lanes/profile.d.ts +48 -0
- package/dist/lib/lanes/profile.js +405 -0
- package/dist/lib/lanes/reconcile.js +25 -3
- package/dist/lib/lanes/registry.d.ts +3 -0
- package/dist/lib/lanes/registry.js +45 -1
- package/dist/lib/lanes/spawn.d.ts +14 -2
- package/dist/lib/lanes/spawn.js +238 -14
- package/dist/lib/lanes/store.js +10 -0
- package/dist/lib/lanes/types.d.ts +11 -1
- package/dist/lib/lanes/types.js +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.142.0] — 2026-09-23
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added reusable personal worker profiles for `hq lanes create --worker`, with
|
|
10
|
+
strict `meta.yaml` validation, content hashes, profile-default precedence,
|
|
11
|
+
provider-specific prompt/settings carriers, and reapplication on every fresh,
|
|
12
|
+
resumed, moved, repaired, and compaction spawn.
|
|
13
|
+
- Personal lanes can now use explicit `--mesh-company` attribution so their
|
|
14
|
+
project thread, presence, and lifecycle events appear in Work Mesh after
|
|
15
|
+
membership validation.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Account login now reuses a registered account home when `--home` is omitted.
|
|
20
|
+
Codex account login also accepts `--device-auth` for remote boxes without a
|
|
21
|
+
localhost browser callback.
|
|
22
|
+
- Checkpoint Stop-gate reply detection now uses the Stop payload when the
|
|
23
|
+
transcript has not flushed yet, and fails open with an ambiguity log when
|
|
24
|
+
neither reply source is reliable.
|
|
25
|
+
|
|
26
|
+
## [5.141.0] — 2026-09-23
|
|
27
|
+
|
|
28
|
+
- `hq bot jobs` now checks the bot's configured agent ID against the machine credential before calling the jobs API. A mismatch reports both IDs and explains how to fix the bot configuration or re-mint its credentials.
|
|
29
|
+
|
|
5
30
|
## [5.140.0] — 2026-09-23
|
|
6
31
|
|
|
7
32
|
### Fixed
|
|
@@ -138,6 +138,19 @@ exec 3>&2
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
input="$(cat 2>/dev/null || printf '{}')"
|
|
141
|
+
input_reply_evidence="$(printf '%s' "$input" | jq -r '
|
|
142
|
+
if has("last_assistant_message") then
|
|
143
|
+
if (.last_assistant_message | type) == "string" then
|
|
144
|
+
"present:" + ((.last_assistant_message | gsub("\\s+"; "") | length) | tostring)
|
|
145
|
+
elif (.last_assistant_message | type) == "array" then
|
|
146
|
+
"present:" + (([.last_assistant_message[]? | (.text? // "")] | join("") | gsub("\\s+"; "") | length) | tostring)
|
|
147
|
+
else
|
|
148
|
+
"invalid"
|
|
149
|
+
end
|
|
150
|
+
else
|
|
151
|
+
"absent"
|
|
152
|
+
end
|
|
153
|
+
' 2>/dev/null || printf 'invalid')"
|
|
141
154
|
input_fields="$(printf '%s' "$input" | jq -r '[.transcript_path // "", .session_id // ""] | @tsv' 2>/dev/null)" || exit 0
|
|
142
155
|
IFS=$'\t' read -r transcript_path session_id <<<"$input_fields" || exit 0
|
|
143
156
|
|
|
@@ -288,6 +301,15 @@ exec 3>&2
|
|
|
288
301
|
session_key="$(hq_hook_safe_session_key "$session_id")"
|
|
289
302
|
[ -n "$session_key" ] || exit 0
|
|
290
303
|
|
|
304
|
+
reply_ambiguity_log="$state_dir/checkpoint-stop-gate-ambiguity.log"
|
|
305
|
+
log_reply_ambiguity() {
|
|
306
|
+
local reason="$1"
|
|
307
|
+
local timestamp
|
|
308
|
+
timestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || printf 'unknown-time')"
|
|
309
|
+
printf '%s\t%s\treply-evidence-ambiguous\t%s\n' "$timestamp" "$session_key" "$reason" \
|
|
310
|
+
>>"$reply_ambiguity_log" 2>/dev/null || true
|
|
311
|
+
}
|
|
312
|
+
|
|
291
313
|
# Parse only the recent JSONL tail. Claude and Codex persist different row
|
|
292
314
|
# shapes, so normalize both to the same user/tool boundary before checking
|
|
293
315
|
# whether the final action was the checkpoint command.
|
|
@@ -469,7 +491,12 @@ exec 3>&2
|
|
|
469
491
|
else (if $seg_reply_chars > 0 then "0-replied" else "0-unreplied" end)
|
|
470
492
|
end
|
|
471
493
|
end
|
|
472
|
-
' 2>/dev/null)"
|
|
494
|
+
' 2>/dev/null)"
|
|
495
|
+
parsed_status=$?
|
|
496
|
+
if [ "$parsed_status" -ne 0 ]; then
|
|
497
|
+
log_reply_ambiguity "transcript-parse-failed"
|
|
498
|
+
exit 0
|
|
499
|
+
fi
|
|
473
500
|
# A Claude success verdict carries the satisfying checkpoint's tool id, the
|
|
474
501
|
# count of non-whitespace assistant text characters delivered after the
|
|
475
502
|
# genuine turn's last work tool (its user-facing reply, wherever it sits
|
|
@@ -494,6 +521,53 @@ exec 3>&2
|
|
|
494
521
|
*) exit 0 ;;
|
|
495
522
|
esac
|
|
496
523
|
|
|
524
|
+
# Claude's Stop payload carries the final assistant message independently of
|
|
525
|
+
# the transcript writer. Use the stronger of the two sources: the transcript
|
|
526
|
+
# can already contain a reply while the payload is empty, and the payload can
|
|
527
|
+
# arrive before the transcript writer flushes. If neither source can prove a
|
|
528
|
+
# reply, the evidence is ambiguous rather than proof that no reply exists.
|
|
529
|
+
# Fail open for the reply demand and record the ambiguity so a host-side
|
|
530
|
+
# flush regression remains observable without forcing a duplicate.
|
|
531
|
+
reply_evidence_ambiguous=0
|
|
532
|
+
case "$reply_chars" in
|
|
533
|
+
''|*[!0-9]*) reply_chars=0 ;;
|
|
534
|
+
esac
|
|
535
|
+
case "$input_reply_evidence" in
|
|
536
|
+
present:*)
|
|
537
|
+
payload_reply_chars="${input_reply_evidence#present:}"
|
|
538
|
+
case "$payload_reply_chars" in
|
|
539
|
+
''|*[!0-9]*) reply_evidence_ambiguous=1 ;;
|
|
540
|
+
*)
|
|
541
|
+
if [ "$runtime" = "claude" ]; then
|
|
542
|
+
if [ "$satisfied" = "1" ]; then
|
|
543
|
+
if [ "$payload_reply_chars" -gt "$reply_chars" ]; then
|
|
544
|
+
reply_chars="$payload_reply_chars"
|
|
545
|
+
fi
|
|
546
|
+
elif [ "$satisfied" = "0" ] && [ "$payload_reply_chars" -gt 0 ]; then
|
|
547
|
+
replied=1
|
|
548
|
+
fi
|
|
549
|
+
fi
|
|
550
|
+
;;
|
|
551
|
+
esac
|
|
552
|
+
;;
|
|
553
|
+
absent|invalid)
|
|
554
|
+
if [ "$runtime" = "claude" ] && [ "$reply_chars" -eq 0 ]; then
|
|
555
|
+
if [ "$satisfied" = "1" ] && [ "$reply_required" = "1" ]; then
|
|
556
|
+
reply_evidence_ambiguous=1
|
|
557
|
+
elif [ "$satisfied" = "0" ]; then
|
|
558
|
+
# A missing checkpoint plus an unflushed reply is also ambiguous;
|
|
559
|
+
# let the already-replied branch record the turn without asking the
|
|
560
|
+
# host to repeat text that may already be visible.
|
|
561
|
+
reply_evidence_ambiguous=1
|
|
562
|
+
replied=1
|
|
563
|
+
fi
|
|
564
|
+
fi
|
|
565
|
+
;;
|
|
566
|
+
esac
|
|
567
|
+
if [ "$reply_evidence_ambiguous" = "1" ]; then
|
|
568
|
+
log_reply_ambiguity "payload=$input_reply_evidence;transcript-reply-chars=$reply_chars"
|
|
569
|
+
fi
|
|
570
|
+
|
|
497
571
|
# Codex tool-result envelopes do not reliably expose a nested shell exit
|
|
498
572
|
# status. The CLI writes this session-scoped stamp only after a successful
|
|
499
573
|
# checkpoint, so require a fresh stamp in addition to the final command.
|
|
@@ -562,7 +636,8 @@ exec 3>&2
|
|
|
562
636
|
# own delivery contract. HQ_CHECKPOINT_REPLY_MIN overrides the minimum
|
|
563
637
|
# non-whitespace character threshold; 0 disables the enforcement.
|
|
564
638
|
if [ "$satisfied" = "1" ] && [ "$runtime" = "claude" ] \
|
|
565
|
-
&& [ "$reply_required" = "1" ] && [ -n "$reply_tool_id" ]
|
|
639
|
+
&& [ "$reply_required" = "1" ] && [ -n "$reply_tool_id" ] \
|
|
640
|
+
&& [ "$reply_evidence_ambiguous" != "1" ]; then
|
|
566
641
|
reply_min="${HQ_CHECKPOINT_REPLY_MIN:-80}"
|
|
567
642
|
case "$reply_min" in ''|*[!0-9]*) reply_min=80 ;; esac
|
|
568
643
|
case "$reply_chars" in ''|*[!0-9]*) reply_chars=0 ;; esac
|
|
@@ -6743,6 +6743,9 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
6743
6743
|
readonly options: readonly [{
|
|
6744
6744
|
readonly flags: "--company <slug|uid>";
|
|
6745
6745
|
readonly description: "Company slug or cloud uid; omit for a core/personal lane";
|
|
6746
|
+
}, {
|
|
6747
|
+
readonly flags: "--mesh-company <slug|uid>";
|
|
6748
|
+
readonly description: "Explicit Work Mesh company for a personal lane; membership is verified";
|
|
6746
6749
|
}, {
|
|
6747
6750
|
readonly flags: "--project <slug>";
|
|
6748
6751
|
readonly description: "HQ project slug";
|
|
@@ -7175,6 +7178,9 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
7175
7178
|
readonly options: readonly [{
|
|
7176
7179
|
readonly flags: "--home <path>";
|
|
7177
7180
|
readonly description: "Account home directory";
|
|
7181
|
+
}, {
|
|
7182
|
+
readonly flags: "--device-auth";
|
|
7183
|
+
readonly description: "Use Codex device authentication instead of localhost callback";
|
|
7178
7184
|
}, {
|
|
7179
7185
|
readonly flags: "--json";
|
|
7180
7186
|
readonly description: "JSON on stdout (always)";
|
|
@@ -8714,6 +8714,10 @@ export const COMMAND_CATALOG = [
|
|
|
8714
8714
|
"flags": "--company <slug|uid>",
|
|
8715
8715
|
"description": "Company slug or cloud uid; omit for a core/personal lane"
|
|
8716
8716
|
},
|
|
8717
|
+
{
|
|
8718
|
+
"flags": "--mesh-company <slug|uid>",
|
|
8719
|
+
"description": "Explicit Work Mesh company for a personal lane; membership is verified"
|
|
8720
|
+
},
|
|
8717
8721
|
{
|
|
8718
8722
|
"flags": "--project <slug>",
|
|
8719
8723
|
"description": "HQ project slug"
|
|
@@ -9278,6 +9282,10 @@ export const COMMAND_CATALOG = [
|
|
|
9278
9282
|
"flags": "--home <path>",
|
|
9279
9283
|
"description": "Account home directory"
|
|
9280
9284
|
},
|
|
9285
|
+
{
|
|
9286
|
+
"flags": "--device-auth",
|
|
9287
|
+
"description": "Use Codex device authentication instead of localhost callback"
|
|
9288
|
+
},
|
|
9281
9289
|
{
|
|
9282
9290
|
"flags": "--json",
|
|
9283
9291
|
"description": "JSON on stdout (always)"
|
|
@@ -20,6 +20,7 @@ import * as fs from "node:fs";
|
|
|
20
20
|
import * as path from "node:path";
|
|
21
21
|
import { BotApi, BotApiError } from "../lib/bot/api.js";
|
|
22
22
|
import { effectiveBotKind } from "../lib/bot/config.js";
|
|
23
|
+
import { identityMismatchMessage, tokenAgentUid } from "../lib/heartbeat-identity.js";
|
|
23
24
|
// ─── Schedule translation ────────────────────────────────────────────────────
|
|
24
25
|
/** Mirrors hq-pro-agents `MIN_RECURRING_INTERVAL_SECONDS`. */
|
|
25
26
|
export const MIN_RECURRING_INTERVAL_SECONDS = 5 * 60;
|
|
@@ -363,10 +364,19 @@ export async function runBotJobs(nameArg, opts, deps) {
|
|
|
363
364
|
const { name, dir, config } = deps.requireBot(nameArg);
|
|
364
365
|
const kind = effectiveBotKind(config);
|
|
365
366
|
const out = deps.out ?? ((line) => console.log(line));
|
|
366
|
-
const api = deps.apiFor
|
|
367
|
-
? deps.apiFor(dir)
|
|
368
|
-
: new BotApi({ token: deps.botTokenSupplier(dir) });
|
|
369
367
|
const ctx = { botName: name, kind };
|
|
368
|
+
let client;
|
|
369
|
+
const jobsApi = async () => {
|
|
370
|
+
if (client)
|
|
371
|
+
return client;
|
|
372
|
+
const token = await deps.botTokenSupplier(dir)();
|
|
373
|
+
const tokenUid = tokenAgentUid(token);
|
|
374
|
+
if (tokenUid && tokenUid !== config.agentUid) {
|
|
375
|
+
refuse(identityMismatchMessage("bot jobs", config.agentUid, tokenUid));
|
|
376
|
+
}
|
|
377
|
+
client = deps.apiFor ? deps.apiFor(dir) : new BotApi({ token: async () => token });
|
|
378
|
+
return client;
|
|
379
|
+
};
|
|
370
380
|
if (opts.add !== undefined && opts.rm !== undefined) {
|
|
371
381
|
refuse("Add a job or remove one, not both in the same command.");
|
|
372
382
|
}
|
|
@@ -374,6 +384,7 @@ export async function runBotJobs(nameArg, opts, deps) {
|
|
|
374
384
|
const jobId = opts.rm.trim();
|
|
375
385
|
if (!jobId)
|
|
376
386
|
refuse("Say which job to remove: --rm <jobId>");
|
|
387
|
+
const api = await jobsApi();
|
|
377
388
|
try {
|
|
378
389
|
await api.cancelJob(config.agentUid, jobId);
|
|
379
390
|
}
|
|
@@ -395,6 +406,7 @@ export async function runBotJobs(nameArg, opts, deps) {
|
|
|
395
406
|
? parse(opts.every, { hqRoot: config.hqRoot, timezone })
|
|
396
407
|
: { cron: opts.cron, timezone };
|
|
397
408
|
const body = buildCreateJobBody({ prompt: opts.add, cron: schedule.cron, timezone: schedule.timezone });
|
|
409
|
+
const api = await jobsApi();
|
|
398
410
|
let created;
|
|
399
411
|
try {
|
|
400
412
|
created = await api.createJob(config.agentUid, body);
|
|
@@ -414,6 +426,7 @@ export async function runBotJobs(nameArg, opts, deps) {
|
|
|
414
426
|
return;
|
|
415
427
|
}
|
|
416
428
|
let jobs;
|
|
429
|
+
const api = await jobsApi();
|
|
417
430
|
try {
|
|
418
431
|
jobs = await api.listJobs(config.agentUid);
|
|
419
432
|
}
|
package/dist/commands/lanes.js
CHANGED
|
@@ -18,7 +18,7 @@ import { vaultApiFetch } from "../utils/vault-api.js";
|
|
|
18
18
|
import { openMeshTransport } from "../lib/mesh/client.js";
|
|
19
19
|
import { createLaneMesh, emitLaneDecisionEvents, } from "../lib/lanes/mesh-events.js";
|
|
20
20
|
import { sessionIdFromEnv } from "../lib/mesh/live/session-identity.js";
|
|
21
|
-
import { AccountResolutionError, coveredLaneIds, DEFAULT_ACCOUNT_HOMES, LANE_PROVIDERS, LANE_STATES, PROVIDER_HOME_ENV, QUESTION_STATUSES, SENIOR_KINDS, accountMaxRunning, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installDrainHook, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, listAccountStatuses, listLaneIds, listQuestionIds, listSessionLiveness, loadLanesConfig, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, MONITOR_ENGINES, MONITOR_MAX_TIMEOUT_MS, MONITOR_UNCOVERED_EXIT_CODE, MAX_WATCHED_LANES, monitorReminderText, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneState, parseLivenessState, parseQuestionStatus, parseSeniorKind, readDaemonPresenceConnected, readLane, retryUpdateLane, readQuestion, resolveAccount, resolveLaneWorker, isLoggedOut, runReconcilePass, runLaneWatch, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, writeLanesConfig, appendDropboxMessage, answerDropboxMessage, deliveryRefusalReason, resumeCommandRefusal, dropboxDir, dropboxInboxPath, laneAcceptsDropboxDelivery, laneAcceptsOperatorMessage, laneDeliveryMechanism, hasRecordedSessionId, providerSupportsSessionResume, laneIdForAnswerDelivery, operatorMessageRefusalReason, raiseLaneQuestion, sha256Text, writeQuestionBody, } from "../lib/lanes/index.js";
|
|
21
|
+
import { AccountResolutionError, coveredLaneIds, DEFAULT_ACCOUNT_HOMES, LANE_PROVIDERS, LANE_STATES, PROVIDER_HOME_ENV, QUESTION_STATUSES, SENIOR_KINDS, accountMaxRunning, accountStatus, appendCappedHistory, canonicalizeHome, createLane, decideAdmission, defaultLivenessDeps, defaultSystemdUserDir, defaultThrottleDeps, installDrainHook, installReconcileTimer, providerPathEnv, probeDrainHookRegistration, isLanesError, isLaneVersionConflict, isTerminalLaneState, listAccountStatuses, listLaneIds, listQuestionIds, listSessionLiveness, loadLanesConfig, MAX_LANE_DEPTH, MONITOR_ERROR_EXIT_CODE, MONITOR_ENGINES, MONITOR_MAX_TIMEOUT_MS, MONITOR_UNCOVERED_EXIT_CODE, MAX_WATCHED_LANES, monitorReminderText, readLiveMonitorEvidence, resolveMachineIdentity as lookupMachineIdentity, loadLivenessIndex, parseLaneProvider, parseLaneState, parseLivenessState, parseQuestionStatus, parseSeniorKind, readDaemonPresenceConnected, readLane, retryUpdateLane, readQuestion, resolveAccount, resolveLaneWorker, ignoredWorkerProfileFields, isLoggedOut, runReconcilePass, runLaneWatch, sessionLiveness, tryReadLane, uninstallReconcileTimer, updateLane, updateQuestion, writeLanesConfig, appendDropboxMessage, answerDropboxMessage, deliveryRefusalReason, resumeCommandRefusal, dropboxDir, dropboxInboxPath, laneAcceptsDropboxDelivery, laneAcceptsOperatorMessage, laneDeliveryMechanism, hasRecordedSessionId, providerSupportsSessionResume, laneIdForAnswerDelivery, operatorMessageRefusalReason, raiseLaneQuestion, sha256Text, writeQuestionBody, } from "../lib/lanes/index.js";
|
|
22
22
|
import { questionBodyAbsPath } from "../lib/lanes/questions.js";
|
|
23
23
|
import { compactionAfterUsage, } from "../lib/lanes/compaction.js";
|
|
24
24
|
import { providerCompactAfterMs } from "../lib/lanes/config.js";
|
|
@@ -735,10 +735,13 @@ function assertSameTenant(laneCompany, senior, seniorCompany, extra = {}) {
|
|
|
735
735
|
...extra,
|
|
736
736
|
});
|
|
737
737
|
}
|
|
738
|
-
function recordMatchesCompany(
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
738
|
+
function recordMatchesCompany(lane, filter) {
|
|
739
|
+
return companyRefMatches(lane.company, filter) || companyRefMatches(lane.mesh_company, filter);
|
|
740
|
+
}
|
|
741
|
+
function companyRefMatches(recordCompany, filter) {
|
|
742
|
+
return (recordCompany !== null &&
|
|
743
|
+
recordCompany !== undefined &&
|
|
744
|
+
(recordCompany.uid === filter.uid || recordCompany.slug === filter.slug));
|
|
742
745
|
}
|
|
743
746
|
function walkHitsLane(hqRoot, start, laneId, ctx) {
|
|
744
747
|
const seen = new Set();
|
|
@@ -828,7 +831,7 @@ function compareQueueOrder(a, b) {
|
|
|
828
831
|
return a.created_at.localeCompare(b.created_at);
|
|
829
832
|
}
|
|
830
833
|
function admitCreate(input) {
|
|
831
|
-
const { hqRoot, provider, pinnedAccount, priority, createdAt, config } = input;
|
|
834
|
+
const { hqRoot, provider, pinnedAccount, profileId, profileMaxConcurrent, priority, createdAt, config, } = input;
|
|
832
835
|
const providerCfg = config.providers[provider];
|
|
833
836
|
const lanes = loadAllLanes(hqRoot).filter((lane) => lane.lane_id !== input.excludeLaneId);
|
|
834
837
|
const runningProvider = lanes.filter((lane) => lane.provider === provider && lane.state === "running");
|
|
@@ -845,9 +848,13 @@ function admitCreate(input) {
|
|
|
845
848
|
const providerRunning = runningProvider.length;
|
|
846
849
|
const queuedCount = queuedSame.length;
|
|
847
850
|
const maxQueued = providerCfg.max_queued;
|
|
851
|
+
const profileRunning = profileId === undefined
|
|
852
|
+
? 0
|
|
853
|
+
: lanes.filter((lane) => lane.worker_profile === profileId && lane.state === "running").length;
|
|
848
854
|
const atCap = providerRunning >= providerCfg.max_running ||
|
|
849
855
|
(pinnedAccount !== undefined &&
|
|
850
|
-
runningCount >= accountMaxRunning(providerCfg, pinnedAccount))
|
|
856
|
+
runningCount >= accountMaxRunning(providerCfg, pinnedAccount)) ||
|
|
857
|
+
(profileMaxConcurrent !== undefined && profileRunning >= profileMaxConcurrent);
|
|
851
858
|
const synthetic = {
|
|
852
859
|
schema_version: "hq-lane.v1",
|
|
853
860
|
lane_id: "__incoming__",
|
|
@@ -1116,6 +1123,15 @@ export function composeWorkerBrief(worker, taskBrief) {
|
|
|
1116
1123
|
"",
|
|
1117
1124
|
"## Knowledge",
|
|
1118
1125
|
formatUnknownValue(yaml.knowledge) || "(none)",
|
|
1126
|
+
...(worker.profile
|
|
1127
|
+
? [
|
|
1128
|
+
"",
|
|
1129
|
+
"## Reusable worker profile",
|
|
1130
|
+
`Profile id: ${worker.profile.id}`,
|
|
1131
|
+
`Profile content hash: ${worker.profile.contentHash}`,
|
|
1132
|
+
"The profile is re-read and applied for every fresh, resumed, moved, and repair spawn.",
|
|
1133
|
+
]
|
|
1134
|
+
: []),
|
|
1119
1135
|
"",
|
|
1120
1136
|
"## Verification checkpoints",
|
|
1121
1137
|
checkpoints || "(none)",
|
|
@@ -1356,6 +1372,7 @@ function mintMessageId() {
|
|
|
1356
1372
|
async function runCreate(ctx, opts) {
|
|
1357
1373
|
const hqRoot = ctx.hqRoot();
|
|
1358
1374
|
const companyFlag = opts.company?.trim();
|
|
1375
|
+
const meshCompanyFlag = opts.meshCompany?.trim();
|
|
1359
1376
|
const project = opts.project?.trim();
|
|
1360
1377
|
const story = opts.story?.trim();
|
|
1361
1378
|
const briefFile = opts.briefFile?.trim();
|
|
@@ -1368,9 +1385,9 @@ async function runCreate(ctx, opts) {
|
|
|
1368
1385
|
if (!briefFile) {
|
|
1369
1386
|
throw new LanesError("missing_brief_file", "--brief-file is required", {});
|
|
1370
1387
|
}
|
|
1371
|
-
const
|
|
1388
|
+
const explicitProvider = opts.provider
|
|
1372
1389
|
? parseLaneProvider(opts.provider)
|
|
1373
|
-
:
|
|
1390
|
+
: undefined;
|
|
1374
1391
|
if (opts.outpost !== undefined && opts.outpost !== "") {
|
|
1375
1392
|
const requested = opts.outpost.trim();
|
|
1376
1393
|
let known;
|
|
@@ -1396,12 +1413,29 @@ async function runCreate(ctx, opts) {
|
|
|
1396
1413
|
const company = companyFlag
|
|
1397
1414
|
? await ctx.resolveCompany(companyFlag)
|
|
1398
1415
|
: null;
|
|
1416
|
+
let meshCompany = company;
|
|
1417
|
+
if (meshCompanyFlag) {
|
|
1418
|
+
const resolvedMeshCompany = await ctx.resolveCompany(meshCompanyFlag);
|
|
1419
|
+
if (company &&
|
|
1420
|
+
(company.uid !== resolvedMeshCompany.uid || company.slug !== resolvedMeshCompany.slug)) {
|
|
1421
|
+
throw new LanesError("mesh_company_mismatch", "--mesh-company must match --company when both are provided", {
|
|
1422
|
+
company: company.slug,
|
|
1423
|
+
mesh_company: resolvedMeshCompany.slug,
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
meshCompany = resolvedMeshCompany;
|
|
1427
|
+
}
|
|
1399
1428
|
const seniorCompany = companyOfSenior(hqRoot, senior, ctx);
|
|
1400
1429
|
assertSameTenant(company, senior, seniorCompany);
|
|
1401
1430
|
let resolvedWorker;
|
|
1402
1431
|
if (opts.worker) {
|
|
1403
1432
|
resolvedWorker = resolveLaneWorker(hqRoot, opts.worker, company?.slug);
|
|
1404
1433
|
}
|
|
1434
|
+
const profile = resolvedWorker?.profile;
|
|
1435
|
+
const provider = explicitProvider ??
|
|
1436
|
+
profile?.meta.provider ??
|
|
1437
|
+
parseLaneProvider("codex");
|
|
1438
|
+
const profileDefaultsApply = profile !== undefined && profile.meta.provider === provider;
|
|
1405
1439
|
const briefAbs = resolveCallerPath(ctx.callerCwd(), briefFile);
|
|
1406
1440
|
if (!ctx.existsSync(briefAbs)) {
|
|
1407
1441
|
throw new LanesError("brief_not_found", `Brief file not found: ${briefAbs}`, {
|
|
@@ -1423,12 +1457,19 @@ async function runCreate(ctx, opts) {
|
|
|
1423
1457
|
const cliModel = parseCliModel(opts.model);
|
|
1424
1458
|
// Validated here because `claude --effort <unknown>` only warns and runs.
|
|
1425
1459
|
const cliEffort = parseCliEffort(provider, opts.effort);
|
|
1460
|
+
const profileModel = profileDefaultsApply ? profile?.meta.model : undefined;
|
|
1461
|
+
const profileEffort = profileDefaultsApply ? profile?.meta.effort : undefined;
|
|
1462
|
+
const profileAccount = profileDefaultsApply ? profile?.meta.account : undefined;
|
|
1463
|
+
const profileAccounts = profileDefaultsApply ? profile?.meta.accounts : undefined;
|
|
1426
1464
|
let pinnedAccount;
|
|
1427
1465
|
const accountWasExplicit = Boolean(opts.account && opts.account.trim() !== "");
|
|
1428
1466
|
if (outpostId) {
|
|
1429
1467
|
if (accountWasExplicit) {
|
|
1430
1468
|
pinnedAccount = opts.account.trim();
|
|
1431
1469
|
}
|
|
1470
|
+
else if (profileAccount !== undefined && profileAccounts === undefined) {
|
|
1471
|
+
pinnedAccount = profileAccount;
|
|
1472
|
+
}
|
|
1432
1473
|
}
|
|
1433
1474
|
else if (opts.account !== undefined) {
|
|
1434
1475
|
const requested = opts.account.trim();
|
|
@@ -1438,9 +1479,27 @@ async function runCreate(ctx, opts) {
|
|
|
1438
1479
|
const resolved = resolveAccount(provider, requested, config, {
|
|
1439
1480
|
hqRoot,
|
|
1440
1481
|
cliModel,
|
|
1482
|
+
model: cliModel ?? profileModel,
|
|
1441
1483
|
});
|
|
1442
1484
|
pinnedAccount = resolved.id;
|
|
1443
1485
|
}
|
|
1486
|
+
else if (profileAccount !== undefined && profileAccounts === undefined) {
|
|
1487
|
+
const resolved = resolveAccount(provider, profileAccount, config, {
|
|
1488
|
+
hqRoot,
|
|
1489
|
+
cliModel,
|
|
1490
|
+
model: cliModel ?? profileModel,
|
|
1491
|
+
});
|
|
1492
|
+
pinnedAccount = resolved.id;
|
|
1493
|
+
}
|
|
1494
|
+
if (outpostId !== undefined && profile !== undefined) {
|
|
1495
|
+
throw new LanesError("worker_profile_outpost_unsupported", `Worker profile ${JSON.stringify(profile.id)} cannot be used with --outpost; personal profiles are local-only`, {
|
|
1496
|
+
worker: profile.id,
|
|
1497
|
+
outpost: outpostId,
|
|
1498
|
+
profile_account: profileAccount ?? null,
|
|
1499
|
+
profile_accounts: profileAccounts ?? null,
|
|
1500
|
+
pinned_account: pinnedAccount ?? null,
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1444
1503
|
const priority = parsePriority(opts.priority);
|
|
1445
1504
|
let timeoutSecs = parseOptionalInt(opts.timeout, "--timeout");
|
|
1446
1505
|
if (timeoutSecs === undefined && resolvedWorker) {
|
|
@@ -1478,6 +1537,9 @@ async function runCreate(ctx, opts) {
|
|
|
1478
1537
|
hqRoot,
|
|
1479
1538
|
existsSync: ctx.existsSync,
|
|
1480
1539
|
cliModel,
|
|
1540
|
+
...(profileAccounts !== undefined
|
|
1541
|
+
? { allowedAccounts: profileAccounts }
|
|
1542
|
+
: {}),
|
|
1481
1543
|
}).id;
|
|
1482
1544
|
const toValidate = pinnedAccount !== undefined
|
|
1483
1545
|
? [candidate]
|
|
@@ -1486,6 +1548,7 @@ async function runCreate(ctx, opts) {
|
|
|
1486
1548
|
...Object.keys(accounts)
|
|
1487
1549
|
.filter((id) => id !== candidate &&
|
|
1488
1550
|
accounts[id]?.enabled &&
|
|
1551
|
+
(profileAccounts === undefined || profileAccounts.includes(id)) &&
|
|
1489
1552
|
accountStatus(provider, id, config, { existsSync: ctx.existsSync }).logged_in === true)
|
|
1490
1553
|
.sort(),
|
|
1491
1554
|
];
|
|
@@ -1548,8 +1611,9 @@ async function runCreate(ctx, opts) {
|
|
|
1548
1611
|
});
|
|
1549
1612
|
}
|
|
1550
1613
|
}
|
|
1551
|
-
const
|
|
1552
|
-
|
|
1614
|
+
const allowedAccounts = validatedAccounts.size > 0 ? [...validatedAccounts] : profileAccounts;
|
|
1615
|
+
const threadId = meshCompany
|
|
1616
|
+
? await ctx.mesh.ensureProjectThread(meshCompany, project)
|
|
1553
1617
|
: undefined;
|
|
1554
1618
|
const persist = async () => {
|
|
1555
1619
|
let admission;
|
|
@@ -1559,7 +1623,7 @@ async function runCreate(ctx, opts) {
|
|
|
1559
1623
|
hqRoot,
|
|
1560
1624
|
existsSync: ctx.existsSync,
|
|
1561
1625
|
cliModel,
|
|
1562
|
-
...(
|
|
1626
|
+
...(allowedAccounts !== undefined ? { allowedAccounts } : {}),
|
|
1563
1627
|
}).id;
|
|
1564
1628
|
}
|
|
1565
1629
|
const accountCfg = pinnedAccount !== undefined
|
|
@@ -1567,6 +1631,7 @@ async function runCreate(ctx, opts) {
|
|
|
1567
1631
|
: undefined;
|
|
1568
1632
|
const model = resolveLaneModel({
|
|
1569
1633
|
cliModel,
|
|
1634
|
+
profileModel,
|
|
1570
1635
|
accountModel: accountCfg?.model,
|
|
1571
1636
|
providerModel: config.providers[provider].model,
|
|
1572
1637
|
});
|
|
@@ -1574,6 +1639,7 @@ async function runCreate(ctx, opts) {
|
|
|
1574
1639
|
// every source except `cli`.
|
|
1575
1640
|
const effort = resolveLaneEffort({
|
|
1576
1641
|
cliEffort,
|
|
1642
|
+
profileEffort,
|
|
1577
1643
|
accountEffort: accountCfg?.effort,
|
|
1578
1644
|
providerEffort: config.providers[provider].effort,
|
|
1579
1645
|
model,
|
|
@@ -1587,6 +1653,10 @@ async function runCreate(ctx, opts) {
|
|
|
1587
1653
|
hqRoot,
|
|
1588
1654
|
provider,
|
|
1589
1655
|
pinnedAccount,
|
|
1656
|
+
...(profile !== undefined ? { profileId: profile.id } : {}),
|
|
1657
|
+
...(profile?.meta.max_concurrent !== undefined
|
|
1658
|
+
? { profileMaxConcurrent: profile.meta.max_concurrent }
|
|
1659
|
+
: {}),
|
|
1590
1660
|
priority,
|
|
1591
1661
|
createdAt,
|
|
1592
1662
|
config,
|
|
@@ -1605,6 +1675,9 @@ async function runCreate(ctx, opts) {
|
|
|
1605
1675
|
const queuedReason = admission.action === "queue" ? admission.reason : undefined;
|
|
1606
1676
|
const lane = createLane(hqRoot, {
|
|
1607
1677
|
company,
|
|
1678
|
+
...(company === null && meshCompany
|
|
1679
|
+
? { mesh_company: meshCompany }
|
|
1680
|
+
: {}),
|
|
1608
1681
|
project_id: project,
|
|
1609
1682
|
story_id: story,
|
|
1610
1683
|
thread_id: threadId,
|
|
@@ -1617,9 +1690,25 @@ async function runCreate(ctx, opts) {
|
|
|
1617
1690
|
worker: resolvedWorker?.id ?? opts.worker,
|
|
1618
1691
|
...(resolvedWorker ? { worker_path: resolvedWorker.path } : {}),
|
|
1619
1692
|
...(resolvedWorker?.pack ? { worker_pack: resolvedWorker.pack } : {}),
|
|
1693
|
+
...(profile
|
|
1694
|
+
? {
|
|
1695
|
+
worker_profile: profile.id,
|
|
1696
|
+
worker_profile_hash: profile.contentHash,
|
|
1697
|
+
worker_profile_ignored: ignoredWorkerProfileFields(provider),
|
|
1698
|
+
}
|
|
1699
|
+
: {}),
|
|
1620
1700
|
provider,
|
|
1621
1701
|
account: pinnedAccount,
|
|
1622
1702
|
...(model !== undefined ? { model } : {}),
|
|
1703
|
+
model_source: cliModel !== undefined
|
|
1704
|
+
? "cli"
|
|
1705
|
+
: profileModel !== undefined
|
|
1706
|
+
? "profile"
|
|
1707
|
+
: accountCfg?.model !== undefined
|
|
1708
|
+
? "account"
|
|
1709
|
+
: config.providers[provider].model !== undefined
|
|
1710
|
+
? "provider"
|
|
1711
|
+
: "none",
|
|
1623
1712
|
...(effort.effort !== undefined ? { effort: effort.effort } : {}),
|
|
1624
1713
|
effort_source: effort.source,
|
|
1625
1714
|
host,
|
|
@@ -1668,13 +1757,13 @@ async function runCreate(ctx, opts) {
|
|
|
1668
1757
|
runCompaction: ctx.runCompaction,
|
|
1669
1758
|
locateTranscript: ctx.locateTranscript,
|
|
1670
1759
|
});
|
|
1671
|
-
if (
|
|
1672
|
-
await emitLaneMeshEvent(ctx,
|
|
1760
|
+
if (meshCompany) {
|
|
1761
|
+
await emitLaneMeshEvent(ctx, meshCompany, threadId, "lane_started", {
|
|
1673
1762
|
lane_id: started.lane_id,
|
|
1674
1763
|
project_id: project,
|
|
1675
1764
|
story_id: story,
|
|
1676
1765
|
});
|
|
1677
|
-
await ctx.mesh.patchStoryStatus(
|
|
1766
|
+
await ctx.mesh.patchStoryStatus(meshCompany, project, story, "in_progress");
|
|
1678
1767
|
}
|
|
1679
1768
|
}
|
|
1680
1769
|
else if (outpostId) {
|
|
@@ -2012,7 +2101,7 @@ async function runList(ctx, opts) {
|
|
|
2012
2101
|
return false;
|
|
2013
2102
|
if (opts.story && lane.story_id !== opts.story)
|
|
2014
2103
|
return false;
|
|
2015
|
-
if (company && !recordMatchesCompany(lane
|
|
2104
|
+
if (company && !recordMatchesCompany(lane, company)) {
|
|
2016
2105
|
return false;
|
|
2017
2106
|
}
|
|
2018
2107
|
if (senior && seniorKey(lane.senior) !== seniorKey(senior))
|
|
@@ -2195,7 +2284,9 @@ async function prepareAccountMove(ctx, lane, input) {
|
|
|
2195
2284
|
const next = {
|
|
2196
2285
|
...current,
|
|
2197
2286
|
account: target,
|
|
2198
|
-
...(input.cliModel !== undefined
|
|
2287
|
+
...(input.cliModel !== undefined
|
|
2288
|
+
? { model: input.cliModel, model_source: "cli" }
|
|
2289
|
+
: {}),
|
|
2199
2290
|
account_history: appendCappedHistory(current.account_history ?? [], {
|
|
2200
2291
|
from,
|
|
2201
2292
|
to: target,
|
|
@@ -2751,14 +2842,14 @@ async function runAsk(ctx, opts) {
|
|
|
2751
2842
|
questionId,
|
|
2752
2843
|
});
|
|
2753
2844
|
questionId = withBody.question_id;
|
|
2754
|
-
await emitLaneMeshEvent(ctx, lane.company, lane.thread_id, "lane_question", {
|
|
2845
|
+
await emitLaneMeshEvent(ctx, lane.mesh_company ?? lane.company, lane.thread_id, "lane_question", {
|
|
2755
2846
|
lane_id: lane.lane_id,
|
|
2756
2847
|
question_id: withBody.question_id,
|
|
2757
2848
|
hop: withBody.hop,
|
|
2758
2849
|
});
|
|
2759
2850
|
const audience = resolveAudience(hqRoot, withBody, ctx);
|
|
2760
2851
|
if (audience.kind === "user") {
|
|
2761
|
-
await emitLaneMeshEvent(ctx, lane.company, lane.thread_id, "lane_question_at_user", {
|
|
2852
|
+
await emitLaneMeshEvent(ctx, lane.mesh_company ?? lane.company, lane.thread_id, "lane_question_at_user", {
|
|
2762
2853
|
lane_id: lane.lane_id,
|
|
2763
2854
|
question_id: withBody.question_id,
|
|
2764
2855
|
});
|
|
@@ -2818,7 +2909,7 @@ async function runQuestionsList(ctx, opts) {
|
|
|
2818
2909
|
return false;
|
|
2819
2910
|
if (opts.story && q.story_id !== opts.story)
|
|
2820
2911
|
return false;
|
|
2821
|
-
if (company && !
|
|
2912
|
+
if (company && !companyRefMatches(q.company, company)) {
|
|
2822
2913
|
return false;
|
|
2823
2914
|
}
|
|
2824
2915
|
if (forFilter) {
|
|
@@ -2984,7 +3075,7 @@ async function runQuestionsEscalate(ctx, questionId, opts) {
|
|
|
2984
3075
|
const nextAudience = resolveAudience(hqRoot, updated, ctx);
|
|
2985
3076
|
if (nextAudience.kind === "user") {
|
|
2986
3077
|
const lane = readLane(hqRoot, updated.lane_id);
|
|
2987
|
-
await emitLaneMeshEvent(ctx, lane.company, lane.thread_id, "lane_question_at_user", { lane_id: lane.lane_id, question_id: updated.question_id });
|
|
3078
|
+
await emitLaneMeshEvent(ctx, lane.mesh_company ?? lane.company, lane.thread_id, "lane_question_at_user", { lane_id: lane.lane_id, question_id: updated.question_id });
|
|
2988
3079
|
}
|
|
2989
3080
|
printJson({ ok: true, action: "questions.escalate", question: updated });
|
|
2990
3081
|
}
|
|
@@ -3063,10 +3154,13 @@ async function runAccountsLogin(ctx, providerRaw, accountId, opts) {
|
|
|
3063
3154
|
if (!id) {
|
|
3064
3155
|
throw new LanesError("invalid_account", "account-id must be non-empty", {});
|
|
3065
3156
|
}
|
|
3157
|
+
if (opts.deviceAuth && provider !== "codex") {
|
|
3158
|
+
throw new LanesError("device_auth_unsupported", "--device-auth is supported only for codex account login", { provider });
|
|
3159
|
+
}
|
|
3160
|
+
const registeredAccount = config.providers[provider].accounts[id];
|
|
3066
3161
|
const homeRaw = opts.home?.trim() ||
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
: `~/.${provider}-${id}`);
|
|
3162
|
+
registeredAccount?.home ||
|
|
3163
|
+
(id === "a" ? DEFAULT_ACCOUNT_HOMES[provider] : `~/.${provider}-${id}`);
|
|
3070
3164
|
const homeResolved = homeRaw.startsWith("~") || path.isAbsolute(homeRaw)
|
|
3071
3165
|
? homeRaw
|
|
3072
3166
|
: resolveCallerPath(callerCwd, homeRaw);
|
|
@@ -3098,14 +3192,17 @@ async function runAccountsLogin(ctx, providerRaw, accountId, opts) {
|
|
|
3098
3192
|
installDrainHook(provider, home);
|
|
3099
3193
|
const envVar = PROVIDER_HOME_ENV[provider];
|
|
3100
3194
|
const login = VENDOR_LOGIN[provider];
|
|
3195
|
+
const loginArgs = provider === "codex" && opts.deviceAuth
|
|
3196
|
+
? [...login.args, "--device-auth"]
|
|
3197
|
+
: login.args;
|
|
3101
3198
|
const result = await ctx.spawnLogin({
|
|
3102
3199
|
command: login.command,
|
|
3103
|
-
args:
|
|
3200
|
+
args: loginArgs,
|
|
3104
3201
|
env: { ...ctx.env, [envVar]: home },
|
|
3105
3202
|
cwd: home,
|
|
3106
3203
|
});
|
|
3107
3204
|
if (result.status !== 0) {
|
|
3108
|
-
throw new LanesError("login_failed", `${login.command} ${
|
|
3205
|
+
throw new LanesError("login_failed", `${login.command} ${loginArgs.join(" ")} exited ${result.status}`, { provider, account: id, status: result.status });
|
|
3109
3206
|
}
|
|
3110
3207
|
const grokNeedsRails = provider === "grok";
|
|
3111
3208
|
if ((grokNeedsRails || copied) && !railsPresent(home)) {
|
|
@@ -3300,6 +3397,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
3300
3397
|
.command("create")
|
|
3301
3398
|
.description("Register a lane, run admission, and return immediately")
|
|
3302
3399
|
.option("--company <slug|uid>", "Company slug or cloud uid; omit for a core/personal lane")
|
|
3400
|
+
.option("--mesh-company <slug|uid>", "Explicit Work Mesh company for a personal lane; membership is verified")
|
|
3303
3401
|
.requiredOption("--project <slug>", "HQ project slug")
|
|
3304
3402
|
.requiredOption("--story <id>", "Story id from the project's prd.json")
|
|
3305
3403
|
.requiredOption("--brief-file <path>", "Path to the lane brief")
|
|
@@ -3472,6 +3570,7 @@ export function registerLanesCommand(parent, deps = {}) {
|
|
|
3472
3570
|
.argument("<provider>", "claude | codex | grok")
|
|
3473
3571
|
.argument("<account-id>", "Open-ended account id")
|
|
3474
3572
|
.option("--home <path>", "Account home directory")
|
|
3573
|
+
.option("--device-auth", "Use Codex device authentication instead of localhost callback")
|
|
3475
3574
|
.action(wrap(async (provider, accountId, opts) => {
|
|
3476
3575
|
await runAccountsLogin(ctx, provider, accountId, opts);
|
|
3477
3576
|
}));
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
15
|
import * as os from "node:os";
|
|
16
16
|
import { CLI_VERSION } from "../../../cli-version.js";
|
|
17
|
-
import { captureHeartbeatIdentityMismatch, isHeartbeatIdentityMismatch, parseHeartbeatFailureBody, tokenAgentUid, } from "../../heartbeat-identity.js";
|
|
17
|
+
import { captureHeartbeatIdentityMismatch, identityMismatchMessage, isHeartbeatIdentityMismatch, parseHeartbeatFailureBody, tokenAgentUid, } from "../../heartbeat-identity.js";
|
|
18
18
|
import { vaultApiFetch } from "../../../utils/vault-api.js";
|
|
19
19
|
import { readComponentStatus } from "../creds.js";
|
|
20
20
|
import { KIT_COMPONENTS } from "../paths.js";
|
|
@@ -90,7 +90,7 @@ async function beatOnceResult(deps) {
|
|
|
90
90
|
...(tokenUid ? { tokenAgentUid: tokenUid } : {}),
|
|
91
91
|
});
|
|
92
92
|
const tokenLabel = tokenUid ?? "unknown";
|
|
93
|
-
deps.log("warn",
|
|
93
|
+
deps.log("warn", identityMismatchMessage("heartbeat", deps.agentUid, tokenLabel));
|
|
94
94
|
(deps.reportIdentityMismatch ?? captureHeartbeatIdentityMismatch)({
|
|
95
95
|
runner: "agent-kit",
|
|
96
96
|
configuredAgentUid: deps.agentUid,
|