@adhdev/daemon-core 0.9.82-rc.455 → 0.9.82-rc.457
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/dist/commands/med-family/mesh-crud.d.ts +19 -0
- package/dist/config/config.d.ts +14 -0
- package/dist/config/registry-resolver.d.ts +54 -0
- package/dist/index.js +369 -67
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +369 -67
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/preview-freshness.d.ts +11 -1
- package/dist/mesh/worktree-bootstrap-config.d.ts +9 -0
- package/dist/providers/approval-utils.d.ts +25 -0
- package/dist/providers/cli-provider-instance.d.ts +30 -1
- package/dist/providers/manual-attendance.d.ts +16 -0
- package/dist/providers/provider-instance.d.ts +8 -1
- package/dist/providers/provider-loader.d.ts +17 -2
- package/dist/providers/spec/fsm-driver.d.ts +5 -0
- package/package.json +3 -3
- package/src/boot/daemon-lifecycle.ts +2 -0
- package/src/commands/handler.ts +9 -5
- package/src/commands/low-family/daemon-lifecycle.ts +14 -1
- package/src/commands/med-family/mesh-crud.ts +83 -49
- package/src/config/config.ts +18 -0
- package/src/config/registry-resolver.ts +100 -0
- package/src/mesh/preview-freshness.ts +46 -1
- package/src/mesh/worktree-bootstrap-config.ts +1 -1
- package/src/providers/approval-utils.ts +42 -0
- package/src/providers/cli-provider-instance.ts +246 -13
- package/src/providers/manual-attendance.ts +20 -0
- package/src/providers/provider-instance.ts +6 -1
- package/src/providers/provider-loader.ts +36 -9
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +13 -2
- package/src/providers/spec/fsm-driver.ts +49 -2
- package/src/commands/WINDOWS-UPGRADE-LOCK-FAILURE.md +0 -198
package/dist/index.js
CHANGED
|
@@ -409,10 +409,10 @@ function readInjected(value) {
|
|
|
409
409
|
}
|
|
410
410
|
function getDaemonBuildInfo() {
|
|
411
411
|
if (cached) return cached;
|
|
412
|
-
const commit = readInjected(true ? "
|
|
413
|
-
const commitShort = readInjected(true ? "
|
|
414
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
415
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
412
|
+
const commit = readInjected(true ? "29441f596e5efe1972b874e0fb5b3d380c9d9bda" : void 0) ?? "unknown";
|
|
413
|
+
const commitShort = readInjected(true ? "29441f59" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
414
|
+
const version = readInjected(true ? "0.9.82-rc.457" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
415
|
+
const builtAt = readInjected(true ? "2026-07-04T08:51:27.287Z" : void 0);
|
|
416
416
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
417
417
|
return cached;
|
|
418
418
|
}
|
|
@@ -1586,6 +1586,8 @@ function normalizeConfig(raw) {
|
|
|
1586
1586
|
ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
|
|
1587
1587
|
providerSourceMode: resolveProviderSourceMode(parsed.providerSourceMode, parsed.disableUpstream),
|
|
1588
1588
|
providerDir: asOptionalString(parsed.providerDir),
|
|
1589
|
+
registryUrl: asOptionalString(parsed.registryUrl),
|
|
1590
|
+
providerTarballUrl: asOptionalString(parsed.providerTarballUrl),
|
|
1589
1591
|
updateChannel: parsed.updateChannel === "preview" ? "preview" : "stable",
|
|
1590
1592
|
terminalSizingMode: parsed.terminalSizingMode === "fit" ? "fit" : "measured"
|
|
1591
1593
|
};
|
|
@@ -8932,6 +8934,7 @@ __export(worktree_bootstrap_config_exports, {
|
|
|
8932
8934
|
WORKTREE_BOOTSTRAP_STALE_RUNNING_MS: () => WORKTREE_BOOTSTRAP_STALE_RUNNING_MS,
|
|
8933
8935
|
computeStaleInputsDigest: () => computeStaleInputsDigest,
|
|
8934
8936
|
evaluateWorktreeBootstrapState: () => evaluateWorktreeBootstrapState,
|
|
8937
|
+
getRegisteredSubmodulePaths: () => getRegisteredSubmodulePaths,
|
|
8935
8938
|
isWorktreeBootstrapStaleRunning: () => isWorktreeBootstrapStaleRunning,
|
|
8936
8939
|
loadMeshWorktreeBootstrapConfig: () => loadMeshWorktreeBootstrapConfig,
|
|
8937
8940
|
runMeshWorktreeBootstrap: () => runMeshWorktreeBootstrap,
|
|
@@ -18418,6 +18421,20 @@ function isNegativeApprovalLabel(value) {
|
|
|
18418
18421
|
function hasNegativeApprovalOption(buttons) {
|
|
18419
18422
|
return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
|
|
18420
18423
|
}
|
|
18424
|
+
function hasReliableApprovalAffirmative(buttons) {
|
|
18425
|
+
return (buttons || []).some((button) => {
|
|
18426
|
+
const label = normalizeApprovalLabel(String(button || ""));
|
|
18427
|
+
if (!label) return false;
|
|
18428
|
+
if (/^always allow\b/.test(label)) return true;
|
|
18429
|
+
if (/^yes\b/.test(label)) {
|
|
18430
|
+
if (/\ballow\b/.test(label)) return true;
|
|
18431
|
+
if (/\bask again\b/.test(label)) return true;
|
|
18432
|
+
if (/\bduring this session\b/.test(label)) return true;
|
|
18433
|
+
if (/\bfrom this project\b/.test(label)) return true;
|
|
18434
|
+
}
|
|
18435
|
+
return false;
|
|
18436
|
+
});
|
|
18437
|
+
}
|
|
18421
18438
|
function getApprovalPositiveHints(provider) {
|
|
18422
18439
|
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
18423
18440
|
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
@@ -20240,7 +20257,7 @@ function scopeLines(spec, lines, questionIndex) {
|
|
|
20240
20257
|
}
|
|
20241
20258
|
}
|
|
20242
20259
|
}
|
|
20243
|
-
if (lastSep >= 0 && prevSep >= 0) {
|
|
20260
|
+
if (lastSep >= 0 && prevSep >= 0 && questionIndex >= prevSep && questionIndex < lastSep + 1) {
|
|
20244
20261
|
return { start: prevSep, end: lastSep + 1 };
|
|
20245
20262
|
}
|
|
20246
20263
|
return {
|
|
@@ -20333,7 +20350,7 @@ var init_parse_approval = __esm({
|
|
|
20333
20350
|
"src/providers/sdk/v1/builders/cli/parse-approval.ts"() {
|
|
20334
20351
|
"use strict";
|
|
20335
20352
|
init_visible_region();
|
|
20336
|
-
SEPARATOR_RE = /^
|
|
20353
|
+
SEPARATOR_RE = /^[─━═╌╍┄┅┈┉]{10,}\s*$/;
|
|
20337
20354
|
}
|
|
20338
20355
|
});
|
|
20339
20356
|
|
|
@@ -30675,6 +30692,35 @@ var DaemonCdpInitializer = class {
|
|
|
30675
30692
|
|
|
30676
30693
|
// src/commands/handler.ts
|
|
30677
30694
|
init_builders();
|
|
30695
|
+
init_config();
|
|
30696
|
+
|
|
30697
|
+
// src/config/registry-resolver.ts
|
|
30698
|
+
var DEFAULT_REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
|
|
30699
|
+
var DEFAULT_PROVIDER_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
|
|
30700
|
+
var REGISTRY_URL_ENV_VAR = "ADHDEV_REGISTRY_URL";
|
|
30701
|
+
var PROVIDER_TARBALL_URL_ENV_VAR = "ADHDEV_PROVIDER_TARBALL_URL";
|
|
30702
|
+
function cleanString(value) {
|
|
30703
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
30704
|
+
}
|
|
30705
|
+
function stripTrailingSlashes(url) {
|
|
30706
|
+
return url.replace(/\/+$/, "");
|
|
30707
|
+
}
|
|
30708
|
+
function resolveRegistryBaseUrl(configuredUrl, env = process.env) {
|
|
30709
|
+
const resolved = cleanString(configuredUrl) ?? cleanString(env[REGISTRY_URL_ENV_VAR]) ?? DEFAULT_REGISTRY_BASE_URL;
|
|
30710
|
+
return stripTrailingSlashes(resolved);
|
|
30711
|
+
}
|
|
30712
|
+
function resolveProviderTarballUrl(configuredUrl, env = process.env) {
|
|
30713
|
+
return cleanString(configuredUrl) ?? cleanString(env[PROVIDER_TARBALL_URL_ENV_VAR]) ?? DEFAULT_PROVIDER_TARBALL_URL;
|
|
30714
|
+
}
|
|
30715
|
+
function resolveProviderTarballTarget(configuredUrl, env = process.env) {
|
|
30716
|
+
const url = resolveProviderTarballUrl(configuredUrl, env);
|
|
30717
|
+
const parsed = new URL(url);
|
|
30718
|
+
return {
|
|
30719
|
+
url,
|
|
30720
|
+
hostname: parsed.hostname,
|
|
30721
|
+
path: parsed.pathname + (parsed.search || "")
|
|
30722
|
+
};
|
|
30723
|
+
}
|
|
30678
30724
|
|
|
30679
30725
|
// src/sessions/reconcile.ts
|
|
30680
30726
|
function upsertSessionTarget(sessionRegistry, target) {
|
|
@@ -30788,6 +30834,10 @@ var MANUAL_ATTENDANCE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
30788
30834
|
"resolve_action",
|
|
30789
30835
|
"pty_input"
|
|
30790
30836
|
]);
|
|
30837
|
+
var MANUAL_ATTENDANCE_PASSIVE_VIEW_COMMANDS = /* @__PURE__ */ new Set([
|
|
30838
|
+
"select_session",
|
|
30839
|
+
"open_panel"
|
|
30840
|
+
]);
|
|
30791
30841
|
|
|
30792
30842
|
// src/chat/chat-signatures.ts
|
|
30793
30843
|
function hashSignatureParts(parts) {
|
|
@@ -35270,13 +35320,14 @@ var DaemonCommandHandler = class {
|
|
|
35270
35320
|
*/
|
|
35271
35321
|
noteManualAttendanceIfApplicable(cmd, args) {
|
|
35272
35322
|
if (!MANUAL_ATTENDANCE_COMMANDS.has(cmd)) return;
|
|
35323
|
+
const passive = MANUAL_ATTENDANCE_PASSIVE_VIEW_COMMANDS.has(cmd);
|
|
35273
35324
|
const sessionId = this._currentRoute.session?.sessionId || (typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "");
|
|
35274
35325
|
if (!sessionId) return;
|
|
35275
35326
|
const session = this._ctx.sessionRegistry?.get(sessionId);
|
|
35276
35327
|
const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
|
|
35277
35328
|
const instance = this._ctx.instanceManager?.getInstance(instanceKey);
|
|
35278
35329
|
try {
|
|
35279
|
-
instance?.noteManualInteraction?.();
|
|
35330
|
+
instance?.noteManualInteraction?.(void 0, { passive });
|
|
35280
35331
|
} catch {
|
|
35281
35332
|
}
|
|
35282
35333
|
}
|
|
@@ -35567,7 +35618,7 @@ var DaemonCommandHandler = class {
|
|
|
35567
35618
|
const https = require("https");
|
|
35568
35619
|
const fs40 = require("fs");
|
|
35569
35620
|
const path44 = require("path");
|
|
35570
|
-
const REGISTRY =
|
|
35621
|
+
const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
|
|
35571
35622
|
function fetchText(url, timeoutMs) {
|
|
35572
35623
|
return new Promise((resolve25, reject) => {
|
|
35573
35624
|
const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: timeoutMs }, (res) => {
|
|
@@ -35929,7 +35980,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
35929
35980
|
const installed = this.handleListInstalledProviders({});
|
|
35930
35981
|
if (!installed.success) return installed;
|
|
35931
35982
|
const https = require("https");
|
|
35932
|
-
const REGISTRY =
|
|
35983
|
+
const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
|
|
35933
35984
|
function fetchJson(url) {
|
|
35934
35985
|
return new Promise((resolve25, reject) => {
|
|
35935
35986
|
const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
|
|
@@ -37532,6 +37583,7 @@ var CHANNEL_SERVER_URL = {
|
|
|
37532
37583
|
stable: "https://api.adhf.dev",
|
|
37533
37584
|
preview: "https://api-preview.adhf.dev"
|
|
37534
37585
|
};
|
|
37586
|
+
var VENDOR_SERVER_URLS = new Set(Object.values(CHANNEL_SERVER_URL));
|
|
37535
37587
|
function normalizeReleaseChannel(value) {
|
|
37536
37588
|
if (typeof value !== "string") return null;
|
|
37537
37589
|
const normalized = value.trim().toLowerCase();
|
|
@@ -37553,7 +37605,11 @@ var daemonLifecycleHandlers = {
|
|
|
37553
37605
|
const npmTag = CHANNEL_NPM_TAG[channel];
|
|
37554
37606
|
const latest = String(execNpmCommandSync(["view", `${pkgName}@${npmTag}`, "version"], { encoding: "utf-8", timeout: 1e4 }, npmSurface)).trim();
|
|
37555
37607
|
LOG.info("Upgrade", `Latest ${pkgName}@${npmTag}: v${latest}`);
|
|
37556
|
-
|
|
37608
|
+
const currentServerUrl = typeof loadConfig().serverUrl === "string" ? loadConfig().serverUrl.trim() : "";
|
|
37609
|
+
const useVendorServerUrl = currentServerUrl === "" || VENDOR_SERVER_URLS.has(currentServerUrl);
|
|
37610
|
+
updateConfig(
|
|
37611
|
+
useVendorServerUrl ? { updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] } : { updateChannel: channel }
|
|
37612
|
+
);
|
|
37557
37613
|
let currentInstalled = null;
|
|
37558
37614
|
try {
|
|
37559
37615
|
const currentJson = String(execNpmCommandSync(["ls", "-g", pkgName, "--depth=0", "--json"], {
|
|
@@ -38261,6 +38317,7 @@ function applyPreLaunchTrust(trust, workingDir) {
|
|
|
38261
38317
|
|
|
38262
38318
|
// src/providers/spec/fsm-driver.ts
|
|
38263
38319
|
init_logger();
|
|
38320
|
+
init_debug_config();
|
|
38264
38321
|
init_pty_write_chunking();
|
|
38265
38322
|
function countNewlines(s2) {
|
|
38266
38323
|
let n = 0;
|
|
@@ -38329,6 +38386,11 @@ var FsmDriver = class {
|
|
|
38329
38386
|
* (−1 = whole screen), or a `section:<id>` / `<region>#ignore:<pat>` string
|
|
38330
38387
|
* when the clause scopes to a section or declares an ignore_lines filter. */
|
|
38331
38388
|
regionLastChangedAt = /* @__PURE__ */ new Map();
|
|
38389
|
+
/** COMPLETION-EARLYNOTIFY stable-eval trace: last stable/not-stable verdict
|
|
38390
|
+
* recorded per stable region, so the trace fires only when the verdict FLIPS
|
|
38391
|
+
* (not every quiet frame). Cleared on every transition alongside
|
|
38392
|
+
* regionLastChangedAt. Diagnostic-only — never consulted by the FSM. */
|
|
38393
|
+
stableVerdictCache = /* @__PURE__ */ new Map();
|
|
38332
38394
|
/** Timer that re-runs evaluate() when a time-condition would flip true
|
|
38333
38395
|
* with no PTY frame to trigger it. */
|
|
38334
38396
|
wakeTimer = null;
|
|
@@ -38664,6 +38726,7 @@ var FsmDriver = class {
|
|
|
38664
38726
|
this.currentStateId = fired.to;
|
|
38665
38727
|
this.stateEnteredAt = now;
|
|
38666
38728
|
this.regionLastChangedAt.clear();
|
|
38729
|
+
this.stableVerdictCache.clear();
|
|
38667
38730
|
this.pushHistory(fired.to, stateById(this.spec, fired.to)?.label ?? fired.to, {
|
|
38668
38731
|
reason: "transition",
|
|
38669
38732
|
via: `${from}\u2192${fired.to}`,
|
|
@@ -38768,6 +38831,7 @@ var FsmDriver = class {
|
|
|
38768
38831
|
trackRegionChanges(currentLines, cursor, now) {
|
|
38769
38832
|
if (this.prevScreenLines.length === 0) return;
|
|
38770
38833
|
const descs = this.stableRegionDescriptors();
|
|
38834
|
+
const stableTraceOn = shouldCollectTraceCategory("fsm-transition");
|
|
38771
38835
|
const needsSections = descs.some((d) => !!d.section);
|
|
38772
38836
|
const curSections = needsSections ? resolveSections(this.spec.sections ?? {}, currentLines) : [];
|
|
38773
38837
|
const prevSections = needsSections ? resolveSections(this.spec.sections ?? {}, this.prevScreenLines) : [];
|
|
@@ -38788,6 +38852,28 @@ var FsmDriver = class {
|
|
|
38788
38852
|
const cur = filterIgnoredLines(curLines, d.ignoreRe).join("\n");
|
|
38789
38853
|
const prev = filterIgnoredLines(prevLines, d.ignoreRe).join("\n");
|
|
38790
38854
|
if (cur !== prev) this.regionLastChangedAt.set(d.key, now);
|
|
38855
|
+
if (stableTraceOn && typeof d.holdMs === "number") {
|
|
38856
|
+
const lastChanged = this.regionLastChangedAt.get(d.key) ?? this.stateEnteredAt;
|
|
38857
|
+
const ageMs = now - lastChanged;
|
|
38858
|
+
const verdict = ageMs >= d.holdMs;
|
|
38859
|
+
if (this.stableVerdictCache.get(d.key) !== verdict) {
|
|
38860
|
+
this.stableVerdictCache.set(d.key, verdict);
|
|
38861
|
+
recordDebugTrace({
|
|
38862
|
+
category: "fsm-transition",
|
|
38863
|
+
stage: "stable-eval",
|
|
38864
|
+
level: "debug",
|
|
38865
|
+
payload: {
|
|
38866
|
+
state: this.currentStateId,
|
|
38867
|
+
regionKey: String(d.key),
|
|
38868
|
+
ignorePattern: d.ignoreRe?.source ?? null,
|
|
38869
|
+
fingerprintLen: cur.length,
|
|
38870
|
+
ageMs,
|
|
38871
|
+
holdMs: d.holdMs,
|
|
38872
|
+
verdict
|
|
38873
|
+
}
|
|
38874
|
+
});
|
|
38875
|
+
}
|
|
38876
|
+
}
|
|
38791
38877
|
}
|
|
38792
38878
|
}
|
|
38793
38879
|
/** Every distinct stable-region descriptor referenced by stable_ms
|
|
@@ -39303,7 +39389,8 @@ function collectStableDescriptors(when, byKey) {
|
|
|
39303
39389
|
const w = when;
|
|
39304
39390
|
if ("stable_ms" in w) {
|
|
39305
39391
|
const key2 = stableRegionKey(w);
|
|
39306
|
-
|
|
39392
|
+
const existing = byKey.get(key2);
|
|
39393
|
+
if (!existing) {
|
|
39307
39394
|
let ignoreRe;
|
|
39308
39395
|
if (w.ignore_lines) {
|
|
39309
39396
|
try {
|
|
@@ -39311,7 +39398,9 @@ function collectStableDescriptors(when, byKey) {
|
|
|
39311
39398
|
} catch {
|
|
39312
39399
|
}
|
|
39313
39400
|
}
|
|
39314
|
-
byKey.set(key2, { key: key2, section: w.section, cursor_above: w.cursor_above, ignoreRe });
|
|
39401
|
+
byKey.set(key2, { key: key2, section: w.section, cursor_above: w.cursor_above, ignoreRe, holdMs: typeof w.stable_ms === "number" ? w.stable_ms : void 0 });
|
|
39402
|
+
} else if (existing.holdMs === void 0 && typeof w.stable_ms === "number") {
|
|
39403
|
+
existing.holdMs = w.stable_ms;
|
|
39315
39404
|
}
|
|
39316
39405
|
return;
|
|
39317
39406
|
}
|
|
@@ -41083,6 +41172,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
|
|
|
41083
41172
|
|
|
41084
41173
|
// src/providers/cli-provider-instance.ts
|
|
41085
41174
|
init_logger();
|
|
41175
|
+
init_debug_config();
|
|
41086
41176
|
init_mesh_event_trace();
|
|
41087
41177
|
init_control_effects();
|
|
41088
41178
|
init_approval_utils();
|
|
@@ -41455,6 +41545,13 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41455
41545
|
// mask is dropped so the real waiting_approval surfaces. Cleared when the episode ends
|
|
41456
41546
|
// (modal genuinely gone, manual attendance takes over, or auto-approve fires).
|
|
41457
41547
|
autoApproveMaskSince = 0;
|
|
41548
|
+
// NOTIF-APPROVAL-MASKED (Q1b): the autoApproveMaskSince episode value for which a
|
|
41549
|
+
// stalled-approval coordinator nudge has already been emitted, so the nudge fires
|
|
41550
|
+
// exactly once per stalled auto-approve episode (0 = none emitted). Reusing the
|
|
41551
|
+
// per-episode mask-clock value as the key makes it provider-agnostic (no reliance on
|
|
41552
|
+
// approvalEntrySeq) and self-resetting: each new episode gets a fresh
|
|
41553
|
+
// autoApproveMaskSince timestamp, and the episode-end reset zeroes it.
|
|
41554
|
+
stalledApprovalNudgeEpisode = 0;
|
|
41458
41555
|
// Provider-common manual-attendance signal: while a human is actively driving
|
|
41459
41556
|
// this session from the dashboard, auto-approve holds so they can take manual
|
|
41460
41557
|
// control. Background mesh workers are never attended → delegated auto-approve
|
|
@@ -42149,9 +42246,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42149
42246
|
return restoredHistory.messages;
|
|
42150
42247
|
}
|
|
42151
42248
|
completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
|
|
42249
|
+
const turnClosed = !this.hasAdapterPendingResponse();
|
|
42152
42250
|
if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
|
|
42153
42251
|
return {
|
|
42154
|
-
present:
|
|
42252
|
+
present: turnClosed,
|
|
42155
42253
|
messages: Array.isArray(parsedMessages) ? parsedMessages : [],
|
|
42156
42254
|
source: "parsed"
|
|
42157
42255
|
};
|
|
@@ -42159,7 +42257,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42159
42257
|
const externalMessages = this.readExternalCompletionMessages();
|
|
42160
42258
|
if (externalMessages) {
|
|
42161
42259
|
return {
|
|
42162
|
-
present: this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
|
|
42260
|
+
present: turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
|
|
42163
42261
|
messages: externalMessages,
|
|
42164
42262
|
source: "external-native"
|
|
42165
42263
|
};
|
|
@@ -42258,11 +42356,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42258
42356
|
if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
|
|
42259
42357
|
const adapterAny = this.adapter;
|
|
42260
42358
|
const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
|
|
42261
|
-
if (!approvalResolvedIdle
|
|
42262
|
-
|
|
42263
|
-
|
|
42264
|
-
if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
|
|
42265
|
-
}
|
|
42359
|
+
if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: !approvalResolvedIdle };
|
|
42360
|
+
if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: !approvalResolvedIdle };
|
|
42361
|
+
if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: !approvalResolvedIdle };
|
|
42266
42362
|
const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
|
|
42267
42363
|
if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
|
|
42268
42364
|
let parsed;
|
|
@@ -42417,6 +42513,39 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42417
42513
|
event
|
|
42418
42514
|
};
|
|
42419
42515
|
}
|
|
42516
|
+
// COMPLETION-EARLYNOTIFY instrumentation. A session-keyed FSM-transition +
|
|
42517
|
+
// completion-gate snapshot recorded into the shared debug-trace ring buffer
|
|
42518
|
+
// (secret-safe, length/role/pattern-name only — never screen or bubble text).
|
|
42519
|
+
// Retrieved via getRecentDebugTrace (chat_debug_bundle). Both categories are a
|
|
42520
|
+
// no-op unless collectDebugTrace is on AND the category is selected, so the
|
|
42521
|
+
// hot-path guards below (completionTraceOn / fsmTraceOn) keep production cost
|
|
42522
|
+
// at a single boolean check.
|
|
42523
|
+
completionTraceOn() {
|
|
42524
|
+
return shouldCollectTraceCategory("completion-gate");
|
|
42525
|
+
}
|
|
42526
|
+
fsmTraceOn() {
|
|
42527
|
+
return shouldCollectTraceCategory("fsm-transition");
|
|
42528
|
+
}
|
|
42529
|
+
recordCompletionGateTrace(stage, payload) {
|
|
42530
|
+
recordDebugTrace({
|
|
42531
|
+
category: "completion-gate",
|
|
42532
|
+
stage,
|
|
42533
|
+
level: "debug",
|
|
42534
|
+
sessionId: this.instanceId,
|
|
42535
|
+
providerType: this.type,
|
|
42536
|
+
payload
|
|
42537
|
+
});
|
|
42538
|
+
}
|
|
42539
|
+
recordFsmTransitionTrace(payload) {
|
|
42540
|
+
recordDebugTrace({
|
|
42541
|
+
category: "fsm-transition",
|
|
42542
|
+
stage: "transition",
|
|
42543
|
+
level: "debug",
|
|
42544
|
+
sessionId: this.instanceId,
|
|
42545
|
+
providerType: this.type,
|
|
42546
|
+
payload
|
|
42547
|
+
});
|
|
42548
|
+
}
|
|
42420
42549
|
flushCompletedDebounceIfFinalized() {
|
|
42421
42550
|
const pending = this.completedDebouncePending;
|
|
42422
42551
|
if (!pending) {
|
|
@@ -42429,12 +42558,27 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42429
42558
|
LOG.debug("CLI", `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
|
|
42430
42559
|
if (latestVisibleStatus !== "idle") {
|
|
42431
42560
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
|
|
42561
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
|
|
42562
|
+
blockReason: "resumed_status",
|
|
42563
|
+
latestVisibleStatus,
|
|
42564
|
+
previousStatus: pending.previousStatus,
|
|
42565
|
+
busyEpochAtArm: pending.busyEpochAtArm,
|
|
42566
|
+
busyEpoch: this.busyEpoch
|
|
42567
|
+
});
|
|
42432
42568
|
this.completedDebouncePending = null;
|
|
42433
42569
|
this.completedDebounceTimer = null;
|
|
42434
42570
|
return;
|
|
42435
42571
|
}
|
|
42436
42572
|
if (typeof pending.busyEpochAtArm === "number" && this.busyEpoch !== pending.busyEpochAtArm) {
|
|
42437
42573
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (busy re-entry during settle: epoch ${pending.busyEpochAtArm}\u2192${this.busyEpoch})`);
|
|
42574
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
|
|
42575
|
+
blockReason: "busy_reentry",
|
|
42576
|
+
latestVisibleStatus,
|
|
42577
|
+
previousStatus: pending.previousStatus,
|
|
42578
|
+
busyEpochAtArm: pending.busyEpochAtArm,
|
|
42579
|
+
busyEpoch: this.busyEpoch,
|
|
42580
|
+
busyEpochDelta: this.busyEpoch - pending.busyEpochAtArm
|
|
42581
|
+
});
|
|
42438
42582
|
this.completedDebouncePending = null;
|
|
42439
42583
|
this.completedDebounceTimer = null;
|
|
42440
42584
|
return;
|
|
@@ -42442,6 +42586,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42442
42586
|
const latestOutputAt = typeof latestStatus?.lastOutputAt === "number" ? latestStatus.lastOutputAt : void 0;
|
|
42443
42587
|
if (typeof pending.lastOutputAtArm === "number" && typeof latestOutputAt === "number" && latestOutputAt > pending.lastOutputAtArm) {
|
|
42444
42588
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (new PTY output during settle: ${pending.lastOutputAtArm}\u2192${latestOutputAt})`);
|
|
42589
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
|
|
42590
|
+
blockReason: "new_pty_output",
|
|
42591
|
+
latestVisibleStatus,
|
|
42592
|
+
previousStatus: pending.previousStatus,
|
|
42593
|
+
lastOutputAtArm: pending.lastOutputAtArm,
|
|
42594
|
+
lastOutputAt: latestOutputAt,
|
|
42595
|
+
lastOutputAtDelta: latestOutputAt - pending.lastOutputAtArm
|
|
42596
|
+
});
|
|
42445
42597
|
this.completedDebouncePending = null;
|
|
42446
42598
|
this.completedDebounceTimer = null;
|
|
42447
42599
|
return;
|
|
@@ -42458,6 +42610,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42458
42610
|
if (this.isMeshWorkerSession()) {
|
|
42459
42611
|
traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
|
|
42460
42612
|
}
|
|
42613
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("hold", {
|
|
42614
|
+
blockReason,
|
|
42615
|
+
latestVisibleStatus,
|
|
42616
|
+
terminal: block2.terminal === true,
|
|
42617
|
+
holdForTranscript: block2.holdForTranscript === true,
|
|
42618
|
+
approvalResolvedIdle: pending.previousStatus === "waiting_approval",
|
|
42619
|
+
waitedMs
|
|
42620
|
+
});
|
|
42461
42621
|
pending.loggedBlockReason = blockReason;
|
|
42462
42622
|
}
|
|
42463
42623
|
this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
|
|
@@ -42477,6 +42637,19 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42477
42637
|
if (this.isMeshWorkerSession()) {
|
|
42478
42638
|
traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
|
|
42479
42639
|
}
|
|
42640
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
|
|
42641
|
+
path: isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "canon_c_decoupled" : "forced_timeout",
|
|
42642
|
+
blockReason,
|
|
42643
|
+
latestVisibleStatus,
|
|
42644
|
+
approvalResolvedIdle: pending.previousStatus === "waiting_approval",
|
|
42645
|
+
finalAssistantPresent: completionDiagnostic.finalAssistantPresent === true,
|
|
42646
|
+
evidenceSource: completionDiagnostic.finalAssistantEvidenceSource ?? null,
|
|
42647
|
+
lastVisibleRole: completionDiagnostic.lastVisibleRole ?? null,
|
|
42648
|
+
lastVisibleContentLen: completionDiagnostic.lastVisibleContentLength ?? null,
|
|
42649
|
+
emittedAfterFinalizationTimeout,
|
|
42650
|
+
waitedMs,
|
|
42651
|
+
busyEpoch: this.busyEpoch
|
|
42652
|
+
});
|
|
42480
42653
|
this.pushEvent({
|
|
42481
42654
|
event: "agent:generating_completed",
|
|
42482
42655
|
chatTitle: pending.chatTitle,
|
|
@@ -42504,6 +42677,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42504
42677
|
if (this.isMeshWorkerSession()) {
|
|
42505
42678
|
traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
|
|
42506
42679
|
}
|
|
42680
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
|
|
42681
|
+
path: "clean",
|
|
42682
|
+
latestVisibleStatus,
|
|
42683
|
+
approvalResolvedIdle: pending.previousStatus === "waiting_approval",
|
|
42684
|
+
finalAssistantPresent: true,
|
|
42685
|
+
duration: pending.duration,
|
|
42686
|
+
busyEpoch: this.busyEpoch
|
|
42687
|
+
});
|
|
42507
42688
|
this.pushEvent({
|
|
42508
42689
|
event: "agent:generating_completed",
|
|
42509
42690
|
chatTitle: pending.chatTitle,
|
|
@@ -42525,6 +42706,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42525
42706
|
this.pendingAutoApprovalSince = 0;
|
|
42526
42707
|
this.autoApproveInactiveSince = 0;
|
|
42527
42708
|
this.autoApproveMaskSince = 0;
|
|
42709
|
+
this.stalledApprovalNudgeEpisode = 0;
|
|
42528
42710
|
if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
|
|
42529
42711
|
this.autoApproveSettleTimer = setTimeout(() => {
|
|
42530
42712
|
this.autoApproveSettleTimer = null;
|
|
@@ -42551,6 +42733,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42551
42733
|
this.pendingAutoApprovalSince = 0;
|
|
42552
42734
|
this.autoApproveInactiveSince = 0;
|
|
42553
42735
|
this.autoApproveMaskSince = 0;
|
|
42736
|
+
this.stalledApprovalNudgeEpisode = 0;
|
|
42554
42737
|
if (this.autoApproveSettleTimer) {
|
|
42555
42738
|
clearTimeout(this.autoApproveSettleTimer);
|
|
42556
42739
|
this.autoApproveSettleTimer = null;
|
|
@@ -42559,6 +42742,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42559
42742
|
}
|
|
42560
42743
|
this.autoApproveInactiveSince = 0;
|
|
42561
42744
|
if (!this.autoApproveMaskSince) this.autoApproveMaskSince = now;
|
|
42745
|
+
this.maybeEmitStalledApprovalNudge(adapterStatus, now);
|
|
42562
42746
|
const modal = adapterStatus.activeModal;
|
|
42563
42747
|
const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
|
|
42564
42748
|
if (!modal || buttons.length === 0) {
|
|
@@ -42569,7 +42753,8 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42569
42753
|
return autoApproveActive;
|
|
42570
42754
|
}
|
|
42571
42755
|
const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(buttons, this.provider);
|
|
42572
|
-
|
|
42756
|
+
const hasReliableConsentAnchor = hasNegativeApprovalOption(buttons) || hasReliableApprovalAffirmative(buttons);
|
|
42757
|
+
if (buttonIndex < 0 || !hasReliableConsentAnchor) {
|
|
42573
42758
|
return autoApproveActive;
|
|
42574
42759
|
}
|
|
42575
42760
|
const modalSignature = [
|
|
@@ -42605,6 +42790,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42605
42790
|
this.pendingAutoApprovalSince = 0;
|
|
42606
42791
|
this.autoApproveInactiveSince = 0;
|
|
42607
42792
|
this.autoApproveMaskSince = 0;
|
|
42793
|
+
this.stalledApprovalNudgeEpisode = 0;
|
|
42608
42794
|
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
42609
42795
|
this.autoApproveBusyTimer = setTimeout(() => {
|
|
42610
42796
|
this.autoApproveBusy = false;
|
|
@@ -42732,6 +42918,18 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42732
42918
|
const previousStatus = this.lastStatus;
|
|
42733
42919
|
if (newStatus !== this.lastStatus) {
|
|
42734
42920
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
42921
|
+
if (this.fsmTraceOn()) this.recordFsmTransitionTrace({
|
|
42922
|
+
from: this.lastStatus,
|
|
42923
|
+
to: newStatus,
|
|
42924
|
+
rawStatus,
|
|
42925
|
+
autoApproveActive,
|
|
42926
|
+
autoApproveHoldIdle,
|
|
42927
|
+
autoApproveBusy: this.autoApproveBusy,
|
|
42928
|
+
hasPending: this.hasAdapterPendingResponse(),
|
|
42929
|
+
busyEpoch: this.busyEpoch,
|
|
42930
|
+
lastOutputAt: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
|
|
42931
|
+
lastScreenChangeAt: typeof adapterStatus?.lastScreenChangeAt === "number" ? adapterStatus.lastScreenChangeAt : null
|
|
42932
|
+
});
|
|
42735
42933
|
const startingToGeneratingWithActiveTurn = this.lastStatus === "starting" && newStatus === "generating" && this.hasAdapterPendingResponse();
|
|
42736
42934
|
if (this.lastStatus === "idle" && newStatus === "generating" || startingToGeneratingWithActiveTurn) {
|
|
42737
42935
|
if (this.completedDebouncePending && this.generatingStartedAt === 0) {
|
|
@@ -42858,6 +43056,17 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42858
43056
|
if (this.isMeshWorkerSession()) {
|
|
42859
43057
|
traceMeshEventStage("arm", this.meshTraceCtx(), `short-generating settle-arm (source=${shortEvidenceSource}, missingEvidence=${missingEvidence})`);
|
|
42860
43058
|
}
|
|
43059
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
|
|
43060
|
+
branch: "short_generating",
|
|
43061
|
+
previousStatus: this.lastStatus,
|
|
43062
|
+
turnStartedAt: shortTurnStartedAt || null,
|
|
43063
|
+
busyEpochAtArm: this.busyEpoch,
|
|
43064
|
+
lastOutputAtArm: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
|
|
43065
|
+
flushDelay: NATIVE_HISTORY_MESH_IDLE_SETTLE_MS,
|
|
43066
|
+
evidenceSource: shortEvidenceSource,
|
|
43067
|
+
missingEvidence,
|
|
43068
|
+
hasFinalSummary: !!shortFinalSummary
|
|
43069
|
+
});
|
|
42861
43070
|
this.scheduleCompletedDebounceFlush(NATIVE_HISTORY_MESH_IDLE_SETTLE_MS);
|
|
42862
43071
|
} else if (missingEvidence) {
|
|
42863
43072
|
LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, non-mesh session (source=${shortEvidenceSource})`);
|
|
@@ -42906,6 +43115,16 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42906
43115
|
const meshSettleSession = this.isAutonomousMeshSession();
|
|
42907
43116
|
const flushDelay = ownsExternalHistory ? meshSettleSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
|
|
42908
43117
|
LOG.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration}s ownsExternalHistory=${ownsExternalHistory} meshSettle=${meshSettleSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
|
|
43118
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
|
|
43119
|
+
branch: "normal",
|
|
43120
|
+
previousStatus: this.completedDebouncePending.previousStatus,
|
|
43121
|
+
turnStartedAt: this.completedDebouncePending.turnStartedAt ?? null,
|
|
43122
|
+
busyEpochAtArm: this.completedDebouncePending.busyEpochAtArm ?? null,
|
|
43123
|
+
lastOutputAtArm: this.completedDebouncePending.lastOutputAtArm ?? null,
|
|
43124
|
+
flushDelay,
|
|
43125
|
+
ownsExternalHistory,
|
|
43126
|
+
meshSettle: meshSettleSession
|
|
43127
|
+
});
|
|
42909
43128
|
this.scheduleCompletedDebounceFlush(flushDelay);
|
|
42910
43129
|
}
|
|
42911
43130
|
} else if (newStatus === "idle" && this.lastStatus === "starting") {
|
|
@@ -43158,7 +43377,8 @@ ${effect.notification.body || ""}`.trim();
|
|
|
43158
43377
|
return false;
|
|
43159
43378
|
}
|
|
43160
43379
|
/** @see ProviderInstance.noteManualInteraction */
|
|
43161
|
-
noteManualInteraction(now = Date.now()) {
|
|
43380
|
+
noteManualInteraction(now = Date.now(), opts) {
|
|
43381
|
+
if (opts?.passive && this.isMeshWorkerSession()) return;
|
|
43162
43382
|
this.manualAttendance.note(now);
|
|
43163
43383
|
}
|
|
43164
43384
|
/**
|
|
@@ -43182,6 +43402,50 @@ ${effect.notification.body || ""}`.trim();
|
|
|
43182
43402
|
autoApproveMaskStalled(now = Date.now()) {
|
|
43183
43403
|
return this.autoApproveMaskSince > 0 && now - this.autoApproveMaskSince > _CliProviderInstance.AUTO_APPROVE_MASK_STALL_MS;
|
|
43184
43404
|
}
|
|
43405
|
+
/**
|
|
43406
|
+
* NOTIF-APPROVAL-MASKED (Q1b): surface a delegated worker's STALLED auto-approve modal
|
|
43407
|
+
* to the mesh COORDINATOR, decoupled from the dashboard visible-status mask.
|
|
43408
|
+
*
|
|
43409
|
+
* When auto-approve is configured but the episode never settles (modal parse miss / the
|
|
43410
|
+
* settle gate never satisfied), getState()/detectStatusTransition() fold the raw
|
|
43411
|
+
* `waiting_approval` into `generating` to suppress dashboard flicker — so
|
|
43412
|
+
* detectStatusTransition()'s `waiting_approval` arm never runs and NO agent:waiting_approval
|
|
43413
|
+
* event is emitted. The coordinator's real-time approval-nudge delivery then has no input and
|
|
43414
|
+
* the worker's stuck modal is never surfaced (the live ~25s stall). The dashboard mask is
|
|
43415
|
+
* intentional and stays; this emits the coordinator nudge exactly ONCE, gated on the SAME
|
|
43416
|
+
* raw-waiting_approval + mask-stalled signal resolveModalParkStatus() distinguishes, the
|
|
43417
|
+
* instant the mask-stall threshold trips (the same moment getState un-folds the mask).
|
|
43418
|
+
*
|
|
43419
|
+
* Only delegated worker sessions qualify: a foreground session has no coordinator to notify,
|
|
43420
|
+
* and its own dashboard mask already reveals the modal on stall. A normally-resolving
|
|
43421
|
+
* auto-approve never reaches AUTO_APPROVE_MASK_STALL_MS, so it emits nothing here; and if a
|
|
43422
|
+
* masked approval clears just as this fires, rc.455's isApprovalNudgeResolved stale-drop
|
|
43423
|
+
* discards the nudge coordinator-side without noise. Dedup is per-episode (keyed on the
|
|
43424
|
+
* mask-clock value) so a modal that flaps between parsed/unparsed states is announced once.
|
|
43425
|
+
*/
|
|
43426
|
+
maybeEmitStalledApprovalNudge(adapterStatus, now) {
|
|
43427
|
+
if (!this.isMeshWorkerSession()) return;
|
|
43428
|
+
if (adapterStatus?.status !== "waiting_approval") return;
|
|
43429
|
+
if (!this.autoApproveMaskStalled(now)) return;
|
|
43430
|
+
if (this.stalledApprovalNudgeEpisode === this.autoApproveMaskSince) return;
|
|
43431
|
+
this.stalledApprovalNudgeEpisode = this.autoApproveMaskSince;
|
|
43432
|
+
const modal = adapterStatus.activeModal;
|
|
43433
|
+
const dirName = workingDirBasename(this.workingDir);
|
|
43434
|
+
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
43435
|
+
this.appendRuntimeSystemMessage(
|
|
43436
|
+
this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
|
|
43437
|
+
`approval_request:${now}`,
|
|
43438
|
+
now
|
|
43439
|
+
);
|
|
43440
|
+
this.pushEvent({
|
|
43441
|
+
event: "agent:waiting_approval",
|
|
43442
|
+
chatTitle,
|
|
43443
|
+
timestamp: now,
|
|
43444
|
+
modalMessage: modal?.message,
|
|
43445
|
+
modalButtons: modal?.buttons
|
|
43446
|
+
});
|
|
43447
|
+
LOG.info("CLI", `[${this.type}] stalled auto-approve nudge \u2192 coordinator (masked ${Math.round((now - this.autoApproveMaskSince) / 1e3)}s)`);
|
|
43448
|
+
}
|
|
43185
43449
|
recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
|
|
43186
43450
|
this.appendRuntimeSystemMessage(
|
|
43187
43451
|
formatAutoApprovalMessage(modalMessage, buttonLabel),
|
|
@@ -47860,12 +48124,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
47860
48124
|
logFn;
|
|
47861
48125
|
versionArchive = null;
|
|
47862
48126
|
scriptsCache = /* @__PURE__ */ new Map();
|
|
48127
|
+
/**
|
|
48128
|
+
* Resolved registry base URL and provider tarball URL. Resolution order:
|
|
48129
|
+
* explicit config field (constructor option) → env var → vendor default.
|
|
48130
|
+
* See `config/registry-resolver.ts`.
|
|
48131
|
+
*/
|
|
48132
|
+
registryBaseUrl;
|
|
48133
|
+
providerTarballUrl;
|
|
47863
48134
|
/** Inject VersionArchive so resolve() can auto-detect installed versions */
|
|
47864
48135
|
setVersionArchive(archive) {
|
|
47865
48136
|
this.versionArchive = archive;
|
|
47866
48137
|
}
|
|
47867
|
-
static GITHUB_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
|
|
47868
|
-
static REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
|
|
47869
48138
|
static META_FILE = ".meta.json";
|
|
47870
48139
|
static REGISTRY_META_FILE = ".registry-meta.json";
|
|
47871
48140
|
static REPO_PROVIDER_DIRNAME = "adhdev-providers";
|
|
@@ -47933,6 +48202,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
47933
48202
|
constructor(options) {
|
|
47934
48203
|
this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
|
|
47935
48204
|
this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
|
|
48205
|
+
this.registryBaseUrl = resolveRegistryBaseUrl(options?.registryUrl);
|
|
48206
|
+
this.providerTarballUrl = resolveProviderTarballUrl(options?.providerTarballUrl);
|
|
47936
48207
|
this.defaultProvidersDir = path35.join(os25.homedir(), ".adhdev", "providers");
|
|
47937
48208
|
const detected = this.detectDefaultUserDir();
|
|
47938
48209
|
this.userDir = detected.path;
|
|
@@ -48965,7 +49236,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48965
49236
|
this.log("Registry sync skipped (sourceMode=no-upstream)");
|
|
48966
49237
|
return { updated: false };
|
|
48967
49238
|
}
|
|
48968
|
-
this.log(`Registry sync starting (${
|
|
49239
|
+
this.log(`Registry sync starting (${this.registryBaseUrl})...`);
|
|
48969
49240
|
const https = require("https");
|
|
48970
49241
|
const regMetaPath = path35.join(this.upstreamDir, _ProviderLoader.REGISTRY_META_FILE);
|
|
48971
49242
|
let cachedChecksums = {};
|
|
@@ -48976,7 +49247,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48976
49247
|
} catch {
|
|
48977
49248
|
}
|
|
48978
49249
|
try {
|
|
48979
|
-
const listUrl = `${
|
|
49250
|
+
const listUrl = `${this.registryBaseUrl}/providers`;
|
|
48980
49251
|
const listBody = await new Promise((resolve25, reject) => {
|
|
48981
49252
|
const req = https.get(listUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
|
|
48982
49253
|
if (res.statusCode !== 200) {
|
|
@@ -49000,7 +49271,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
49000
49271
|
const { type, category, checksum, version } = entry;
|
|
49001
49272
|
const cacheKey = `${category}/${type}`;
|
|
49002
49273
|
if (cachedChecksums[cacheKey] === checksum) continue;
|
|
49003
|
-
const dlUrl = `${
|
|
49274
|
+
const dlUrl = `${this.registryBaseUrl}/providers/${type}/${version}/download`;
|
|
49004
49275
|
const manifestBody = await new Promise((resolve25, reject) => {
|
|
49005
49276
|
const req = https.get(dlUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 3e4 }, (res) => {
|
|
49006
49277
|
if (res.statusCode !== 200) {
|
|
@@ -49067,12 +49338,13 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
49067
49338
|
this.log("Upstream check skipped (last check < 30min ago)");
|
|
49068
49339
|
return { updated: false };
|
|
49069
49340
|
}
|
|
49341
|
+
const tarballTarget = resolveProviderTarballTarget(this.providerTarballUrl);
|
|
49070
49342
|
try {
|
|
49071
49343
|
const etag = await new Promise((resolve25, reject) => {
|
|
49072
49344
|
const options = {
|
|
49073
49345
|
method: "HEAD",
|
|
49074
|
-
hostname:
|
|
49075
|
-
path:
|
|
49346
|
+
hostname: tarballTarget.hostname,
|
|
49347
|
+
path: tarballTarget.path,
|
|
49076
49348
|
headers: { "User-Agent": "adhdev-launcher" },
|
|
49077
49349
|
timeout: 1e4
|
|
49078
49350
|
};
|
|
@@ -49113,7 +49385,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
49113
49385
|
this.log("Downloading latest providers from GitHub...");
|
|
49114
49386
|
const tmpTar = path35.join(os25.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
|
|
49115
49387
|
const tmpExtract = path35.join(os25.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
|
|
49116
|
-
await this.downloadFile(
|
|
49388
|
+
await this.downloadFile(tarballTarget.url, tmpTar);
|
|
49117
49389
|
fs25.mkdirSync(tmpExtract, { recursive: true });
|
|
49118
49390
|
await execAsync5(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
|
|
49119
49391
|
const extracted = fs25.readdirSync(tmpExtract);
|
|
@@ -49213,7 +49485,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
49213
49485
|
etag,
|
|
49214
49486
|
timestamp,
|
|
49215
49487
|
lastCheck: new Date(timestamp).toISOString(),
|
|
49216
|
-
source:
|
|
49488
|
+
source: this.providerTarballUrl
|
|
49217
49489
|
}, null, 2));
|
|
49218
49490
|
} catch {
|
|
49219
49491
|
}
|
|
@@ -50325,6 +50597,47 @@ async function decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg) {
|
|
|
50325
50597
|
if (await isAncestor(worktreeOssSha, sourceSha)) return "advance";
|
|
50326
50598
|
return "skip_diverged";
|
|
50327
50599
|
}
|
|
50600
|
+
async function syncClonedWorktreeSubmodules(worktreePath, sourceWorkspace, rg) {
|
|
50601
|
+
const submodulePaths = getRegisteredSubmodulePaths(worktreePath);
|
|
50602
|
+
if (submodulePaths.size === 0) return;
|
|
50603
|
+
const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
|
|
50604
|
+
const worktreeCtx = { workspace: worktreePath, repoRoot: worktreePath, isGitRepo: true };
|
|
50605
|
+
const readStdout = (out) => (typeof out === "string" ? out : out?.stdout ?? "").trim();
|
|
50606
|
+
for (const submodulePath of submodulePaths) {
|
|
50607
|
+
try {
|
|
50608
|
+
const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", submodulePath], { timeoutMs: 1e4 });
|
|
50609
|
+
const sourceSha = readStdout(sourceStatusOut).match(/^[+\- ]?([0-9a-f]{40})/)?.[1];
|
|
50610
|
+
if (!sourceSha) continue;
|
|
50611
|
+
const subCtx = {
|
|
50612
|
+
workspace: `${worktreePath}/${submodulePath}`,
|
|
50613
|
+
repoRoot: `${worktreePath}/${submodulePath}`,
|
|
50614
|
+
isGitRepo: true
|
|
50615
|
+
};
|
|
50616
|
+
const worktreeSubSha = readStdout(await rg(subCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 }));
|
|
50617
|
+
if (!worktreeSubSha || worktreeSubSha === sourceSha) continue;
|
|
50618
|
+
await rg(subCtx, ["fetch", `${sourceWorkspace}/${submodulePath}`, "HEAD"], { timeoutMs: 6e4 });
|
|
50619
|
+
let action;
|
|
50620
|
+
try {
|
|
50621
|
+
action = await decideOssCloneSync(subCtx, worktreeSubSha, sourceSha, rg);
|
|
50622
|
+
} catch (decideErr) {
|
|
50623
|
+
action = "skip_diverged";
|
|
50624
|
+
console.warn(`[mesh] ${submodulePath} submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
|
|
50625
|
+
}
|
|
50626
|
+
if (action === "advance") {
|
|
50627
|
+
await rg(subCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
|
|
50628
|
+
await rg(worktreeCtx, ["add", submodulePath], { timeoutMs: 1e4 });
|
|
50629
|
+
await rg(worktreeCtx, ["commit", "-m", `chore: sync ${submodulePath} to source node HEAD on clone`], { timeoutMs: 1e4 });
|
|
50630
|
+
console.log(`[mesh] Advanced ${submodulePath} submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
|
|
50631
|
+
} else if (action === "skip_rewind") {
|
|
50632
|
+
console.warn(`[mesh] Skipped ${submodulePath} submodule rewind on clone: source node ${submodulePath} ${sourceSha.slice(0, 8)} is an ancestor of the fresh worktree ${submodulePath} ${worktreeSubSha.slice(0, 8)} \u2014 kept fresher worktree HEAD`);
|
|
50633
|
+
} else if (action === "skip_diverged") {
|
|
50634
|
+
console.warn(`[mesh] Skipped ${submodulePath} submodule sync on clone: source node ${submodulePath} ${sourceSha.slice(0, 8)} diverged from the fresh worktree ${submodulePath} ${worktreeSubSha.slice(0, 8)} \u2014 kept worktree HEAD (coordinator reconciles)`);
|
|
50635
|
+
}
|
|
50636
|
+
} catch (subErr) {
|
|
50637
|
+
console.warn(`[mesh] ${submodulePath} submodule sync to source HEAD failed (best-effort):`, subErr?.message ?? subErr);
|
|
50638
|
+
}
|
|
50639
|
+
}
|
|
50640
|
+
}
|
|
50328
50641
|
var meshCrudHandlers = {
|
|
50329
50642
|
list_meshes: async (ctx, _args) => {
|
|
50330
50643
|
try {
|
|
@@ -51029,42 +51342,8 @@ var meshCrudHandlers = {
|
|
|
51029
51342
|
submodulesInitialized2 = true;
|
|
51030
51343
|
const sourceWorkspace = sourceNode.repoRoot || sourceNode.workspace;
|
|
51031
51344
|
if (sourceWorkspace) {
|
|
51032
|
-
|
|
51033
|
-
|
|
51034
|
-
const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
|
|
51035
|
-
const worktreeCtx = { workspace: result.worktreePath, repoRoot: result.worktreePath, isGitRepo: true };
|
|
51036
|
-
const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", "oss"], { timeoutMs: 1e4 });
|
|
51037
|
-
const sourceStatusLine = (typeof sourceStatusOut === "string" ? sourceStatusOut : sourceStatusOut?.stdout ?? "").trim();
|
|
51038
|
-
const sourceShaMatch = sourceStatusLine.match(/^[+\- ]?([0-9a-f]{40})/);
|
|
51039
|
-
const sourceSha = sourceShaMatch?.[1];
|
|
51040
|
-
if (sourceSha) {
|
|
51041
|
-
const ossCtx = { workspace: `${result.worktreePath}/oss`, repoRoot: `${result.worktreePath}/oss`, isGitRepo: true };
|
|
51042
|
-
const worktreeOssHeadOut = await rg(ossCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 });
|
|
51043
|
-
const worktreeOssSha = (typeof worktreeOssHeadOut === "string" ? worktreeOssHeadOut : worktreeOssHeadOut?.stdout ?? "").trim();
|
|
51044
|
-
if (worktreeOssSha && worktreeOssSha !== sourceSha) {
|
|
51045
|
-
await rg(ossCtx, ["fetch", `${sourceWorkspace}/oss`, "HEAD"], { timeoutMs: 6e4 });
|
|
51046
|
-
let ossAction;
|
|
51047
|
-
try {
|
|
51048
|
-
ossAction = await decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg);
|
|
51049
|
-
} catch (decideErr) {
|
|
51050
|
-
ossAction = "skip_diverged";
|
|
51051
|
-
console.warn(`[mesh] oss submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
|
|
51052
|
-
}
|
|
51053
|
-
if (ossAction === "advance") {
|
|
51054
|
-
await rg(ossCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
|
|
51055
|
-
await rg(worktreeCtx, ["add", "oss"], { timeoutMs: 1e4 });
|
|
51056
|
-
await rg(worktreeCtx, ["commit", "-m", "chore: sync oss to source node HEAD on clone"], { timeoutMs: 1e4 });
|
|
51057
|
-
console.log(`[mesh] Advanced oss submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
|
|
51058
|
-
} else if (ossAction === "skip_rewind") {
|
|
51059
|
-
console.warn(`[mesh] Skipped oss submodule rewind on clone: source node oss ${sourceSha.slice(0, 8)} is an ancestor of the fresh worktree oss ${worktreeOssSha.slice(0, 8)} \u2014 kept fresher worktree HEAD`);
|
|
51060
|
-
} else if (ossAction === "skip_diverged") {
|
|
51061
|
-
console.warn(`[mesh] Skipped oss submodule sync on clone: source node oss ${sourceSha.slice(0, 8)} diverged from the fresh worktree oss ${worktreeOssSha.slice(0, 8)} \u2014 kept worktree HEAD (coordinator reconciles)`);
|
|
51062
|
-
}
|
|
51063
|
-
}
|
|
51064
|
-
}
|
|
51065
|
-
} catch (ossErr) {
|
|
51066
|
-
console.warn("[mesh] oss submodule sync to source HEAD failed (best-effort):", ossErr.message);
|
|
51067
|
-
}
|
|
51345
|
+
const { runGit: rg } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
|
|
51346
|
+
await syncClonedWorktreeSubmodules(result.worktreePath, sourceWorkspace, rg);
|
|
51068
51347
|
}
|
|
51069
51348
|
} catch (subErr) {
|
|
51070
51349
|
console.warn("[mesh] Submodule init failed for worktree:", subErr.message);
|
|
@@ -52498,6 +52777,26 @@ var import_node_child_process4 = require("child_process");
|
|
|
52498
52777
|
var import_node_fs4 = require("fs");
|
|
52499
52778
|
var import_node_path2 = require("path");
|
|
52500
52779
|
var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
|
|
52780
|
+
var PREVIEW_PIPELINE_SCRIPTS = [
|
|
52781
|
+
"scripts/preview-freshness.mjs",
|
|
52782
|
+
"scripts/smoke-preview-web.mjs",
|
|
52783
|
+
"scripts/deploy-preview-local.mjs"
|
|
52784
|
+
];
|
|
52785
|
+
function hasDeployPreviewNpmScript(repoRoot) {
|
|
52786
|
+
const pkgPath = (0, import_node_path2.resolve)(repoRoot, "package.json");
|
|
52787
|
+
if (!(0, import_node_fs4.existsSync)(pkgPath)) return false;
|
|
52788
|
+
try {
|
|
52789
|
+
const pkg = JSON.parse((0, import_node_fs4.readFileSync)(pkgPath, "utf8"));
|
|
52790
|
+
return typeof pkg?.scripts?.["deploy:preview"] === "string";
|
|
52791
|
+
} catch {
|
|
52792
|
+
return false;
|
|
52793
|
+
}
|
|
52794
|
+
}
|
|
52795
|
+
function isPreviewPipelineConfigured(repoRoot) {
|
|
52796
|
+
if ((0, import_node_fs4.existsSync)((0, import_node_path2.resolve)(repoRoot, PREVIEW_DEPLOY_RECORD))) return true;
|
|
52797
|
+
if (PREVIEW_PIPELINE_SCRIPTS.some((rel) => (0, import_node_fs4.existsSync)((0, import_node_path2.resolve)(repoRoot, rel)))) return true;
|
|
52798
|
+
return hasDeployPreviewNpmScript(repoRoot);
|
|
52799
|
+
}
|
|
52501
52800
|
function runGit2(repoRoot, args) {
|
|
52502
52801
|
try {
|
|
52503
52802
|
return (0, import_node_child_process4.execFileSync)("git", args, {
|
|
@@ -52549,6 +52848,7 @@ function readCurrentMainCommit(repoRoot) {
|
|
|
52549
52848
|
return { currentMainCommit: null, currentMainCommitSource: "unknown" };
|
|
52550
52849
|
}
|
|
52551
52850
|
function buildPreviewFreshness(repoRoot) {
|
|
52851
|
+
if (!isPreviewPipelineConfigured(repoRoot)) return null;
|
|
52552
52852
|
const current = readCurrentMainCommit(repoRoot);
|
|
52553
52853
|
const record = readRecord6(repoRoot);
|
|
52554
52854
|
const lastPreviewCommit = normalizeCommit(record?.lastPreviewCommit);
|
|
@@ -66259,7 +66559,9 @@ async function initDaemonComponents(config) {
|
|
|
66259
66559
|
const providerLoader = new ProviderLoader({
|
|
66260
66560
|
logFn: config.providerLogFn,
|
|
66261
66561
|
sourceMode: providerSourceMode,
|
|
66262
|
-
userDir: appConfig.providerDir
|
|
66562
|
+
userDir: appConfig.providerDir,
|
|
66563
|
+
registryUrl: appConfig.registryUrl,
|
|
66564
|
+
providerTarballUrl: appConfig.providerTarballUrl
|
|
66263
66565
|
});
|
|
66264
66566
|
providerLoader.loadAll();
|
|
66265
66567
|
providerLoader.registerToDetector();
|