@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.mjs
CHANGED
|
@@ -404,10 +404,10 @@ function readInjected(value) {
|
|
|
404
404
|
}
|
|
405
405
|
function getDaemonBuildInfo() {
|
|
406
406
|
if (cached) return cached;
|
|
407
|
-
const commit = readInjected(true ? "
|
|
408
|
-
const commitShort = readInjected(true ? "
|
|
409
|
-
const version = readInjected(true ? "0.9.82-rc.
|
|
410
|
-
const builtAt = readInjected(true ? "2026-07-
|
|
407
|
+
const commit = readInjected(true ? "29441f596e5efe1972b874e0fb5b3d380c9d9bda" : void 0) ?? "unknown";
|
|
408
|
+
const commitShort = readInjected(true ? "29441f59" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
409
|
+
const version = readInjected(true ? "0.9.82-rc.457" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
410
|
+
const builtAt = readInjected(true ? "2026-07-04T08:51:27.287Z" : void 0);
|
|
411
411
|
cached = builtAt ? { commit, commitShort, version, builtAt } : { commit, commitShort, version };
|
|
412
412
|
return cached;
|
|
413
413
|
}
|
|
@@ -1585,6 +1585,8 @@ function normalizeConfig(raw) {
|
|
|
1585
1585
|
ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
|
|
1586
1586
|
providerSourceMode: resolveProviderSourceMode(parsed.providerSourceMode, parsed.disableUpstream),
|
|
1587
1587
|
providerDir: asOptionalString(parsed.providerDir),
|
|
1588
|
+
registryUrl: asOptionalString(parsed.registryUrl),
|
|
1589
|
+
providerTarballUrl: asOptionalString(parsed.providerTarballUrl),
|
|
1588
1590
|
updateChannel: parsed.updateChannel === "preview" ? "preview" : "stable",
|
|
1589
1591
|
terminalSizingMode: parsed.terminalSizingMode === "fit" ? "fit" : "measured"
|
|
1590
1592
|
};
|
|
@@ -8925,6 +8927,7 @@ __export(worktree_bootstrap_config_exports, {
|
|
|
8925
8927
|
WORKTREE_BOOTSTRAP_STALE_RUNNING_MS: () => WORKTREE_BOOTSTRAP_STALE_RUNNING_MS,
|
|
8926
8928
|
computeStaleInputsDigest: () => computeStaleInputsDigest,
|
|
8927
8929
|
evaluateWorktreeBootstrapState: () => evaluateWorktreeBootstrapState,
|
|
8930
|
+
getRegisteredSubmodulePaths: () => getRegisteredSubmodulePaths,
|
|
8928
8931
|
isWorktreeBootstrapStaleRunning: () => isWorktreeBootstrapStaleRunning,
|
|
8929
8932
|
loadMeshWorktreeBootstrapConfig: () => loadMeshWorktreeBootstrapConfig,
|
|
8930
8933
|
runMeshWorktreeBootstrap: () => runMeshWorktreeBootstrap,
|
|
@@ -18413,6 +18416,20 @@ function isNegativeApprovalLabel(value) {
|
|
|
18413
18416
|
function hasNegativeApprovalOption(buttons) {
|
|
18414
18417
|
return (buttons || []).some((button) => isNegativeApprovalLabel(String(button || "")));
|
|
18415
18418
|
}
|
|
18419
|
+
function hasReliableApprovalAffirmative(buttons) {
|
|
18420
|
+
return (buttons || []).some((button) => {
|
|
18421
|
+
const label = normalizeApprovalLabel(String(button || ""));
|
|
18422
|
+
if (!label) return false;
|
|
18423
|
+
if (/^always allow\b/.test(label)) return true;
|
|
18424
|
+
if (/^yes\b/.test(label)) {
|
|
18425
|
+
if (/\ballow\b/.test(label)) return true;
|
|
18426
|
+
if (/\bask again\b/.test(label)) return true;
|
|
18427
|
+
if (/\bduring this session\b/.test(label)) return true;
|
|
18428
|
+
if (/\bfrom this project\b/.test(label)) return true;
|
|
18429
|
+
}
|
|
18430
|
+
return false;
|
|
18431
|
+
});
|
|
18432
|
+
}
|
|
18416
18433
|
function getApprovalPositiveHints(provider) {
|
|
18417
18434
|
const customHints = Array.isArray(provider?.approvalPositiveHints) ? provider.approvalPositiveHints.map((hint) => normalizeApprovalLabel(String(hint || ""))).filter(Boolean) : [];
|
|
18418
18435
|
return customHints.length > 0 ? customHints : DEFAULT_APPROVAL_POSITIVE_HINTS;
|
|
@@ -20234,7 +20251,7 @@ function scopeLines(spec, lines, questionIndex) {
|
|
|
20234
20251
|
}
|
|
20235
20252
|
}
|
|
20236
20253
|
}
|
|
20237
|
-
if (lastSep >= 0 && prevSep >= 0) {
|
|
20254
|
+
if (lastSep >= 0 && prevSep >= 0 && questionIndex >= prevSep && questionIndex < lastSep + 1) {
|
|
20238
20255
|
return { start: prevSep, end: lastSep + 1 };
|
|
20239
20256
|
}
|
|
20240
20257
|
return {
|
|
@@ -20327,7 +20344,7 @@ var init_parse_approval = __esm({
|
|
|
20327
20344
|
"src/providers/sdk/v1/builders/cli/parse-approval.ts"() {
|
|
20328
20345
|
"use strict";
|
|
20329
20346
|
init_visible_region();
|
|
20330
|
-
SEPARATOR_RE = /^
|
|
20347
|
+
SEPARATOR_RE = /^[─━═╌╍┄┅┈┉]{10,}\s*$/;
|
|
20331
20348
|
}
|
|
20332
20349
|
});
|
|
20333
20350
|
|
|
@@ -30268,6 +30285,35 @@ var DaemonCdpInitializer = class {
|
|
|
30268
30285
|
|
|
30269
30286
|
// src/commands/handler.ts
|
|
30270
30287
|
init_builders();
|
|
30288
|
+
init_config();
|
|
30289
|
+
|
|
30290
|
+
// src/config/registry-resolver.ts
|
|
30291
|
+
var DEFAULT_REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
|
|
30292
|
+
var DEFAULT_PROVIDER_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
|
|
30293
|
+
var REGISTRY_URL_ENV_VAR = "ADHDEV_REGISTRY_URL";
|
|
30294
|
+
var PROVIDER_TARBALL_URL_ENV_VAR = "ADHDEV_PROVIDER_TARBALL_URL";
|
|
30295
|
+
function cleanString(value) {
|
|
30296
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
30297
|
+
}
|
|
30298
|
+
function stripTrailingSlashes(url) {
|
|
30299
|
+
return url.replace(/\/+$/, "");
|
|
30300
|
+
}
|
|
30301
|
+
function resolveRegistryBaseUrl(configuredUrl, env = process.env) {
|
|
30302
|
+
const resolved = cleanString(configuredUrl) ?? cleanString(env[REGISTRY_URL_ENV_VAR]) ?? DEFAULT_REGISTRY_BASE_URL;
|
|
30303
|
+
return stripTrailingSlashes(resolved);
|
|
30304
|
+
}
|
|
30305
|
+
function resolveProviderTarballUrl(configuredUrl, env = process.env) {
|
|
30306
|
+
return cleanString(configuredUrl) ?? cleanString(env[PROVIDER_TARBALL_URL_ENV_VAR]) ?? DEFAULT_PROVIDER_TARBALL_URL;
|
|
30307
|
+
}
|
|
30308
|
+
function resolveProviderTarballTarget(configuredUrl, env = process.env) {
|
|
30309
|
+
const url = resolveProviderTarballUrl(configuredUrl, env);
|
|
30310
|
+
const parsed = new URL(url);
|
|
30311
|
+
return {
|
|
30312
|
+
url,
|
|
30313
|
+
hostname: parsed.hostname,
|
|
30314
|
+
path: parsed.pathname + (parsed.search || "")
|
|
30315
|
+
};
|
|
30316
|
+
}
|
|
30271
30317
|
|
|
30272
30318
|
// src/sessions/reconcile.ts
|
|
30273
30319
|
function upsertSessionTarget(sessionRegistry, target) {
|
|
@@ -30381,6 +30427,10 @@ var MANUAL_ATTENDANCE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
30381
30427
|
"resolve_action",
|
|
30382
30428
|
"pty_input"
|
|
30383
30429
|
]);
|
|
30430
|
+
var MANUAL_ATTENDANCE_PASSIVE_VIEW_COMMANDS = /* @__PURE__ */ new Set([
|
|
30431
|
+
"select_session",
|
|
30432
|
+
"open_panel"
|
|
30433
|
+
]);
|
|
30384
30434
|
|
|
30385
30435
|
// src/chat/chat-signatures.ts
|
|
30386
30436
|
function hashSignatureParts(parts) {
|
|
@@ -34863,13 +34913,14 @@ var DaemonCommandHandler = class {
|
|
|
34863
34913
|
*/
|
|
34864
34914
|
noteManualAttendanceIfApplicable(cmd, args) {
|
|
34865
34915
|
if (!MANUAL_ATTENDANCE_COMMANDS.has(cmd)) return;
|
|
34916
|
+
const passive = MANUAL_ATTENDANCE_PASSIVE_VIEW_COMMANDS.has(cmd);
|
|
34866
34917
|
const sessionId = this._currentRoute.session?.sessionId || (typeof args?.targetSessionId === "string" ? args.targetSessionId.trim() : "");
|
|
34867
34918
|
if (!sessionId) return;
|
|
34868
34919
|
const session = this._ctx.sessionRegistry?.get(sessionId);
|
|
34869
34920
|
const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
|
|
34870
34921
|
const instance = this._ctx.instanceManager?.getInstance(instanceKey);
|
|
34871
34922
|
try {
|
|
34872
|
-
instance?.noteManualInteraction?.();
|
|
34923
|
+
instance?.noteManualInteraction?.(void 0, { passive });
|
|
34873
34924
|
} catch {
|
|
34874
34925
|
}
|
|
34875
34926
|
}
|
|
@@ -35160,7 +35211,7 @@ var DaemonCommandHandler = class {
|
|
|
35160
35211
|
const https = __require("https");
|
|
35161
35212
|
const fs40 = __require("fs");
|
|
35162
35213
|
const path44 = __require("path");
|
|
35163
|
-
const REGISTRY =
|
|
35214
|
+
const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
|
|
35164
35215
|
function fetchText(url, timeoutMs) {
|
|
35165
35216
|
return new Promise((resolve25, reject) => {
|
|
35166
35217
|
const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: timeoutMs }, (res) => {
|
|
@@ -35522,7 +35573,7 @@ ${formatManifestValidationIssues2(validation.issues)}`,
|
|
|
35522
35573
|
const installed = this.handleListInstalledProviders({});
|
|
35523
35574
|
if (!installed.success) return installed;
|
|
35524
35575
|
const https = __require("https");
|
|
35525
|
-
const REGISTRY =
|
|
35576
|
+
const REGISTRY = resolveRegistryBaseUrl(loadConfig().registryUrl);
|
|
35526
35577
|
function fetchJson(url) {
|
|
35527
35578
|
return new Promise((resolve25, reject) => {
|
|
35528
35579
|
const req = https.get(url, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
|
|
@@ -37125,6 +37176,7 @@ var CHANNEL_SERVER_URL = {
|
|
|
37125
37176
|
stable: "https://api.adhf.dev",
|
|
37126
37177
|
preview: "https://api-preview.adhf.dev"
|
|
37127
37178
|
};
|
|
37179
|
+
var VENDOR_SERVER_URLS = new Set(Object.values(CHANNEL_SERVER_URL));
|
|
37128
37180
|
function normalizeReleaseChannel(value) {
|
|
37129
37181
|
if (typeof value !== "string") return null;
|
|
37130
37182
|
const normalized = value.trim().toLowerCase();
|
|
@@ -37146,7 +37198,11 @@ var daemonLifecycleHandlers = {
|
|
|
37146
37198
|
const npmTag = CHANNEL_NPM_TAG[channel];
|
|
37147
37199
|
const latest = String(execNpmCommandSync(["view", `${pkgName}@${npmTag}`, "version"], { encoding: "utf-8", timeout: 1e4 }, npmSurface)).trim();
|
|
37148
37200
|
LOG.info("Upgrade", `Latest ${pkgName}@${npmTag}: v${latest}`);
|
|
37149
|
-
|
|
37201
|
+
const currentServerUrl = typeof loadConfig().serverUrl === "string" ? loadConfig().serverUrl.trim() : "";
|
|
37202
|
+
const useVendorServerUrl = currentServerUrl === "" || VENDOR_SERVER_URLS.has(currentServerUrl);
|
|
37203
|
+
updateConfig(
|
|
37204
|
+
useVendorServerUrl ? { updateChannel: channel, serverUrl: CHANNEL_SERVER_URL[channel] } : { updateChannel: channel }
|
|
37205
|
+
);
|
|
37150
37206
|
let currentInstalled = null;
|
|
37151
37207
|
try {
|
|
37152
37208
|
const currentJson = String(execNpmCommandSync(["ls", "-g", pkgName, "--depth=0", "--json"], {
|
|
@@ -37854,6 +37910,7 @@ function applyPreLaunchTrust(trust, workingDir) {
|
|
|
37854
37910
|
|
|
37855
37911
|
// src/providers/spec/fsm-driver.ts
|
|
37856
37912
|
init_logger();
|
|
37913
|
+
init_debug_config();
|
|
37857
37914
|
init_pty_write_chunking();
|
|
37858
37915
|
function countNewlines(s2) {
|
|
37859
37916
|
let n = 0;
|
|
@@ -37922,6 +37979,11 @@ var FsmDriver = class {
|
|
|
37922
37979
|
* (−1 = whole screen), or a `section:<id>` / `<region>#ignore:<pat>` string
|
|
37923
37980
|
* when the clause scopes to a section or declares an ignore_lines filter. */
|
|
37924
37981
|
regionLastChangedAt = /* @__PURE__ */ new Map();
|
|
37982
|
+
/** COMPLETION-EARLYNOTIFY stable-eval trace: last stable/not-stable verdict
|
|
37983
|
+
* recorded per stable region, so the trace fires only when the verdict FLIPS
|
|
37984
|
+
* (not every quiet frame). Cleared on every transition alongside
|
|
37985
|
+
* regionLastChangedAt. Diagnostic-only — never consulted by the FSM. */
|
|
37986
|
+
stableVerdictCache = /* @__PURE__ */ new Map();
|
|
37925
37987
|
/** Timer that re-runs evaluate() when a time-condition would flip true
|
|
37926
37988
|
* with no PTY frame to trigger it. */
|
|
37927
37989
|
wakeTimer = null;
|
|
@@ -38257,6 +38319,7 @@ var FsmDriver = class {
|
|
|
38257
38319
|
this.currentStateId = fired.to;
|
|
38258
38320
|
this.stateEnteredAt = now;
|
|
38259
38321
|
this.regionLastChangedAt.clear();
|
|
38322
|
+
this.stableVerdictCache.clear();
|
|
38260
38323
|
this.pushHistory(fired.to, stateById(this.spec, fired.to)?.label ?? fired.to, {
|
|
38261
38324
|
reason: "transition",
|
|
38262
38325
|
via: `${from}\u2192${fired.to}`,
|
|
@@ -38361,6 +38424,7 @@ var FsmDriver = class {
|
|
|
38361
38424
|
trackRegionChanges(currentLines, cursor, now) {
|
|
38362
38425
|
if (this.prevScreenLines.length === 0) return;
|
|
38363
38426
|
const descs = this.stableRegionDescriptors();
|
|
38427
|
+
const stableTraceOn = shouldCollectTraceCategory("fsm-transition");
|
|
38364
38428
|
const needsSections = descs.some((d) => !!d.section);
|
|
38365
38429
|
const curSections = needsSections ? resolveSections(this.spec.sections ?? {}, currentLines) : [];
|
|
38366
38430
|
const prevSections = needsSections ? resolveSections(this.spec.sections ?? {}, this.prevScreenLines) : [];
|
|
@@ -38381,6 +38445,28 @@ var FsmDriver = class {
|
|
|
38381
38445
|
const cur = filterIgnoredLines(curLines, d.ignoreRe).join("\n");
|
|
38382
38446
|
const prev = filterIgnoredLines(prevLines, d.ignoreRe).join("\n");
|
|
38383
38447
|
if (cur !== prev) this.regionLastChangedAt.set(d.key, now);
|
|
38448
|
+
if (stableTraceOn && typeof d.holdMs === "number") {
|
|
38449
|
+
const lastChanged = this.regionLastChangedAt.get(d.key) ?? this.stateEnteredAt;
|
|
38450
|
+
const ageMs = now - lastChanged;
|
|
38451
|
+
const verdict = ageMs >= d.holdMs;
|
|
38452
|
+
if (this.stableVerdictCache.get(d.key) !== verdict) {
|
|
38453
|
+
this.stableVerdictCache.set(d.key, verdict);
|
|
38454
|
+
recordDebugTrace({
|
|
38455
|
+
category: "fsm-transition",
|
|
38456
|
+
stage: "stable-eval",
|
|
38457
|
+
level: "debug",
|
|
38458
|
+
payload: {
|
|
38459
|
+
state: this.currentStateId,
|
|
38460
|
+
regionKey: String(d.key),
|
|
38461
|
+
ignorePattern: d.ignoreRe?.source ?? null,
|
|
38462
|
+
fingerprintLen: cur.length,
|
|
38463
|
+
ageMs,
|
|
38464
|
+
holdMs: d.holdMs,
|
|
38465
|
+
verdict
|
|
38466
|
+
}
|
|
38467
|
+
});
|
|
38468
|
+
}
|
|
38469
|
+
}
|
|
38384
38470
|
}
|
|
38385
38471
|
}
|
|
38386
38472
|
/** Every distinct stable-region descriptor referenced by stable_ms
|
|
@@ -38896,7 +38982,8 @@ function collectStableDescriptors(when, byKey) {
|
|
|
38896
38982
|
const w = when;
|
|
38897
38983
|
if ("stable_ms" in w) {
|
|
38898
38984
|
const key2 = stableRegionKey(w);
|
|
38899
|
-
|
|
38985
|
+
const existing = byKey.get(key2);
|
|
38986
|
+
if (!existing) {
|
|
38900
38987
|
let ignoreRe;
|
|
38901
38988
|
if (w.ignore_lines) {
|
|
38902
38989
|
try {
|
|
@@ -38904,7 +38991,9 @@ function collectStableDescriptors(when, byKey) {
|
|
|
38904
38991
|
} catch {
|
|
38905
38992
|
}
|
|
38906
38993
|
}
|
|
38907
|
-
byKey.set(key2, { key: key2, section: w.section, cursor_above: w.cursor_above, ignoreRe });
|
|
38994
|
+
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 });
|
|
38995
|
+
} else if (existing.holdMs === void 0 && typeof w.stable_ms === "number") {
|
|
38996
|
+
existing.holdMs = w.stable_ms;
|
|
38908
38997
|
}
|
|
38909
38998
|
return;
|
|
38910
38999
|
}
|
|
@@ -40676,6 +40765,7 @@ function createCliAdapter(provider, workingDir, cliArgs, extraEnv, transportFact
|
|
|
40676
40765
|
|
|
40677
40766
|
// src/providers/cli-provider-instance.ts
|
|
40678
40767
|
init_logger();
|
|
40768
|
+
init_debug_config();
|
|
40679
40769
|
init_mesh_event_trace();
|
|
40680
40770
|
init_control_effects();
|
|
40681
40771
|
init_approval_utils();
|
|
@@ -41048,6 +41138,13 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41048
41138
|
// mask is dropped so the real waiting_approval surfaces. Cleared when the episode ends
|
|
41049
41139
|
// (modal genuinely gone, manual attendance takes over, or auto-approve fires).
|
|
41050
41140
|
autoApproveMaskSince = 0;
|
|
41141
|
+
// NOTIF-APPROVAL-MASKED (Q1b): the autoApproveMaskSince episode value for which a
|
|
41142
|
+
// stalled-approval coordinator nudge has already been emitted, so the nudge fires
|
|
41143
|
+
// exactly once per stalled auto-approve episode (0 = none emitted). Reusing the
|
|
41144
|
+
// per-episode mask-clock value as the key makes it provider-agnostic (no reliance on
|
|
41145
|
+
// approvalEntrySeq) and self-resetting: each new episode gets a fresh
|
|
41146
|
+
// autoApproveMaskSince timestamp, and the episode-end reset zeroes it.
|
|
41147
|
+
stalledApprovalNudgeEpisode = 0;
|
|
41051
41148
|
// Provider-common manual-attendance signal: while a human is actively driving
|
|
41052
41149
|
// this session from the dashboard, auto-approve holds so they can take manual
|
|
41053
41150
|
// control. Background mesh workers are never attended → delegated auto-approve
|
|
@@ -41742,9 +41839,10 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41742
41839
|
return restoredHistory.messages;
|
|
41743
41840
|
}
|
|
41744
41841
|
completionFinalAssistantEvidence(parsedMessages, turnStartedAt) {
|
|
41842
|
+
const turnClosed = !this.hasAdapterPendingResponse();
|
|
41745
41843
|
if (this.completionHasFinalAssistantMessage(parsedMessages, turnStartedAt)) {
|
|
41746
41844
|
return {
|
|
41747
|
-
present:
|
|
41845
|
+
present: turnClosed,
|
|
41748
41846
|
messages: Array.isArray(parsedMessages) ? parsedMessages : [],
|
|
41749
41847
|
source: "parsed"
|
|
41750
41848
|
};
|
|
@@ -41752,7 +41850,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41752
41850
|
const externalMessages = this.readExternalCompletionMessages();
|
|
41753
41851
|
if (externalMessages) {
|
|
41754
41852
|
return {
|
|
41755
|
-
present: this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
|
|
41853
|
+
present: turnClosed && this.completionHasFinalAssistantMessage(externalMessages, turnStartedAt),
|
|
41756
41854
|
messages: externalMessages,
|
|
41757
41855
|
source: "external-native"
|
|
41758
41856
|
};
|
|
@@ -41851,11 +41949,9 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
41851
41949
|
if (latestVisibleStatus !== "idle") return { reason: `status:${latestVisibleStatus}`, terminal: true };
|
|
41852
41950
|
const adapterAny = this.adapter;
|
|
41853
41951
|
const approvalResolvedIdle = pending.previousStatus === "waiting_approval";
|
|
41854
|
-
if (!approvalResolvedIdle
|
|
41855
|
-
|
|
41856
|
-
|
|
41857
|
-
if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: true };
|
|
41858
|
-
}
|
|
41952
|
+
if (adapterAny?.isWaitingForResponse === true) return { reason: "adapter_waiting_for_response", terminal: !approvalResolvedIdle };
|
|
41953
|
+
if (adapterAny?.currentTurnScope) return { reason: "adapter_turn_scope_active", terminal: !approvalResolvedIdle };
|
|
41954
|
+
if (this.hasAdapterPendingResponse()) return { reason: "adapter_pending_response", terminal: !approvalResolvedIdle };
|
|
41859
41955
|
const partial = typeof this.adapter.getPartialResponse === "function" ? this.adapter.getPartialResponse() : "";
|
|
41860
41956
|
if (typeof partial === "string" && partial.trim()) return { reason: "partial_response_pending", terminal: true };
|
|
41861
41957
|
let parsed;
|
|
@@ -42010,6 +42106,39 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42010
42106
|
event
|
|
42011
42107
|
};
|
|
42012
42108
|
}
|
|
42109
|
+
// COMPLETION-EARLYNOTIFY instrumentation. A session-keyed FSM-transition +
|
|
42110
|
+
// completion-gate snapshot recorded into the shared debug-trace ring buffer
|
|
42111
|
+
// (secret-safe, length/role/pattern-name only — never screen or bubble text).
|
|
42112
|
+
// Retrieved via getRecentDebugTrace (chat_debug_bundle). Both categories are a
|
|
42113
|
+
// no-op unless collectDebugTrace is on AND the category is selected, so the
|
|
42114
|
+
// hot-path guards below (completionTraceOn / fsmTraceOn) keep production cost
|
|
42115
|
+
// at a single boolean check.
|
|
42116
|
+
completionTraceOn() {
|
|
42117
|
+
return shouldCollectTraceCategory("completion-gate");
|
|
42118
|
+
}
|
|
42119
|
+
fsmTraceOn() {
|
|
42120
|
+
return shouldCollectTraceCategory("fsm-transition");
|
|
42121
|
+
}
|
|
42122
|
+
recordCompletionGateTrace(stage, payload) {
|
|
42123
|
+
recordDebugTrace({
|
|
42124
|
+
category: "completion-gate",
|
|
42125
|
+
stage,
|
|
42126
|
+
level: "debug",
|
|
42127
|
+
sessionId: this.instanceId,
|
|
42128
|
+
providerType: this.type,
|
|
42129
|
+
payload
|
|
42130
|
+
});
|
|
42131
|
+
}
|
|
42132
|
+
recordFsmTransitionTrace(payload) {
|
|
42133
|
+
recordDebugTrace({
|
|
42134
|
+
category: "fsm-transition",
|
|
42135
|
+
stage: "transition",
|
|
42136
|
+
level: "debug",
|
|
42137
|
+
sessionId: this.instanceId,
|
|
42138
|
+
providerType: this.type,
|
|
42139
|
+
payload
|
|
42140
|
+
});
|
|
42141
|
+
}
|
|
42013
42142
|
flushCompletedDebounceIfFinalized() {
|
|
42014
42143
|
const pending = this.completedDebouncePending;
|
|
42015
42144
|
if (!pending) {
|
|
@@ -42022,12 +42151,27 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42022
42151
|
LOG.debug("CLI", `[${this.type}] flush attempt: adapterStatus=${latestStatus.status} latestVisible=${latestVisibleStatus} generatingStartedAt=${this.generatingStartedAt} isWaitingForResponse=${!!this.adapter?.isWaitingForResponse} hasPartial=${!!this.adapter.getPartialResponse?.()}`);
|
|
42023
42152
|
if (latestVisibleStatus !== "idle") {
|
|
42024
42153
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (resumed ${latestVisibleStatus})`);
|
|
42154
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
|
|
42155
|
+
blockReason: "resumed_status",
|
|
42156
|
+
latestVisibleStatus,
|
|
42157
|
+
previousStatus: pending.previousStatus,
|
|
42158
|
+
busyEpochAtArm: pending.busyEpochAtArm,
|
|
42159
|
+
busyEpoch: this.busyEpoch
|
|
42160
|
+
});
|
|
42025
42161
|
this.completedDebouncePending = null;
|
|
42026
42162
|
this.completedDebounceTimer = null;
|
|
42027
42163
|
return;
|
|
42028
42164
|
}
|
|
42029
42165
|
if (typeof pending.busyEpochAtArm === "number" && this.busyEpoch !== pending.busyEpochAtArm) {
|
|
42030
42166
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (busy re-entry during settle: epoch ${pending.busyEpochAtArm}\u2192${this.busyEpoch})`);
|
|
42167
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
|
|
42168
|
+
blockReason: "busy_reentry",
|
|
42169
|
+
latestVisibleStatus,
|
|
42170
|
+
previousStatus: pending.previousStatus,
|
|
42171
|
+
busyEpochAtArm: pending.busyEpochAtArm,
|
|
42172
|
+
busyEpoch: this.busyEpoch,
|
|
42173
|
+
busyEpochDelta: this.busyEpoch - pending.busyEpochAtArm
|
|
42174
|
+
});
|
|
42031
42175
|
this.completedDebouncePending = null;
|
|
42032
42176
|
this.completedDebounceTimer = null;
|
|
42033
42177
|
return;
|
|
@@ -42035,6 +42179,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42035
42179
|
const latestOutputAt = typeof latestStatus?.lastOutputAt === "number" ? latestStatus.lastOutputAt : void 0;
|
|
42036
42180
|
if (typeof pending.lastOutputAtArm === "number" && typeof latestOutputAt === "number" && latestOutputAt > pending.lastOutputAtArm) {
|
|
42037
42181
|
LOG.info("CLI", `[${this.type}] cancelled pending completed (new PTY output during settle: ${pending.lastOutputAtArm}\u2192${latestOutputAt})`);
|
|
42182
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("cancel", {
|
|
42183
|
+
blockReason: "new_pty_output",
|
|
42184
|
+
latestVisibleStatus,
|
|
42185
|
+
previousStatus: pending.previousStatus,
|
|
42186
|
+
lastOutputAtArm: pending.lastOutputAtArm,
|
|
42187
|
+
lastOutputAt: latestOutputAt,
|
|
42188
|
+
lastOutputAtDelta: latestOutputAt - pending.lastOutputAtArm
|
|
42189
|
+
});
|
|
42038
42190
|
this.completedDebouncePending = null;
|
|
42039
42191
|
this.completedDebounceTimer = null;
|
|
42040
42192
|
return;
|
|
@@ -42051,6 +42203,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42051
42203
|
if (this.isMeshWorkerSession()) {
|
|
42052
42204
|
traceMeshEventDrop("completion_gate_hold", this.meshTraceCtx(), `${blockReason} waited=${waitedMs}ms`);
|
|
42053
42205
|
}
|
|
42206
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("hold", {
|
|
42207
|
+
blockReason,
|
|
42208
|
+
latestVisibleStatus,
|
|
42209
|
+
terminal: block2.terminal === true,
|
|
42210
|
+
holdForTranscript: block2.holdForTranscript === true,
|
|
42211
|
+
approvalResolvedIdle: pending.previousStatus === "waiting_approval",
|
|
42212
|
+
waitedMs
|
|
42213
|
+
});
|
|
42054
42214
|
pending.loggedBlockReason = blockReason;
|
|
42055
42215
|
}
|
|
42056
42216
|
this.scheduleCompletedDebounceFlush(COMPLETED_FINALIZATION_RETRY_MS);
|
|
@@ -42070,6 +42230,19 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42070
42230
|
if (this.isMeshWorkerSession()) {
|
|
42071
42231
|
traceMeshEventStage("fired", this.meshTraceCtx(), `forced after ${waitedMs}ms (${blockReason})`);
|
|
42072
42232
|
}
|
|
42233
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
|
|
42234
|
+
path: isTranscriptEvidenceGate && !emittedAfterFinalizationTimeout ? "canon_c_decoupled" : "forced_timeout",
|
|
42235
|
+
blockReason,
|
|
42236
|
+
latestVisibleStatus,
|
|
42237
|
+
approvalResolvedIdle: pending.previousStatus === "waiting_approval",
|
|
42238
|
+
finalAssistantPresent: completionDiagnostic.finalAssistantPresent === true,
|
|
42239
|
+
evidenceSource: completionDiagnostic.finalAssistantEvidenceSource ?? null,
|
|
42240
|
+
lastVisibleRole: completionDiagnostic.lastVisibleRole ?? null,
|
|
42241
|
+
lastVisibleContentLen: completionDiagnostic.lastVisibleContentLength ?? null,
|
|
42242
|
+
emittedAfterFinalizationTimeout,
|
|
42243
|
+
waitedMs,
|
|
42244
|
+
busyEpoch: this.busyEpoch
|
|
42245
|
+
});
|
|
42073
42246
|
this.pushEvent({
|
|
42074
42247
|
event: "agent:generating_completed",
|
|
42075
42248
|
chatTitle: pending.chatTitle,
|
|
@@ -42097,6 +42270,14 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42097
42270
|
if (this.isMeshWorkerSession()) {
|
|
42098
42271
|
traceMeshEventStage("fired", this.meshTraceCtx(), `duration=${pending.duration}s`);
|
|
42099
42272
|
}
|
|
42273
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("fire", {
|
|
42274
|
+
path: "clean",
|
|
42275
|
+
latestVisibleStatus,
|
|
42276
|
+
approvalResolvedIdle: pending.previousStatus === "waiting_approval",
|
|
42277
|
+
finalAssistantPresent: true,
|
|
42278
|
+
duration: pending.duration,
|
|
42279
|
+
busyEpoch: this.busyEpoch
|
|
42280
|
+
});
|
|
42100
42281
|
this.pushEvent({
|
|
42101
42282
|
event: "agent:generating_completed",
|
|
42102
42283
|
chatTitle: pending.chatTitle,
|
|
@@ -42118,6 +42299,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42118
42299
|
this.pendingAutoApprovalSince = 0;
|
|
42119
42300
|
this.autoApproveInactiveSince = 0;
|
|
42120
42301
|
this.autoApproveMaskSince = 0;
|
|
42302
|
+
this.stalledApprovalNudgeEpisode = 0;
|
|
42121
42303
|
if (this.autoApproveSettleTimer) clearTimeout(this.autoApproveSettleTimer);
|
|
42122
42304
|
this.autoApproveSettleTimer = setTimeout(() => {
|
|
42123
42305
|
this.autoApproveSettleTimer = null;
|
|
@@ -42144,6 +42326,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42144
42326
|
this.pendingAutoApprovalSince = 0;
|
|
42145
42327
|
this.autoApproveInactiveSince = 0;
|
|
42146
42328
|
this.autoApproveMaskSince = 0;
|
|
42329
|
+
this.stalledApprovalNudgeEpisode = 0;
|
|
42147
42330
|
if (this.autoApproveSettleTimer) {
|
|
42148
42331
|
clearTimeout(this.autoApproveSettleTimer);
|
|
42149
42332
|
this.autoApproveSettleTimer = null;
|
|
@@ -42152,6 +42335,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42152
42335
|
}
|
|
42153
42336
|
this.autoApproveInactiveSince = 0;
|
|
42154
42337
|
if (!this.autoApproveMaskSince) this.autoApproveMaskSince = now;
|
|
42338
|
+
this.maybeEmitStalledApprovalNudge(adapterStatus, now);
|
|
42155
42339
|
const modal = adapterStatus.activeModal;
|
|
42156
42340
|
const buttons = Array.isArray(modal?.buttons) ? modal.buttons.map((b) => String(b || "").trim()).filter(Boolean) : [];
|
|
42157
42341
|
if (!modal || buttons.length === 0) {
|
|
@@ -42162,7 +42346,8 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42162
42346
|
return autoApproveActive;
|
|
42163
42347
|
}
|
|
42164
42348
|
const { index: buttonIndex, label: buttonLabel } = pickApprovalButton(buttons, this.provider);
|
|
42165
|
-
|
|
42349
|
+
const hasReliableConsentAnchor = hasNegativeApprovalOption(buttons) || hasReliableApprovalAffirmative(buttons);
|
|
42350
|
+
if (buttonIndex < 0 || !hasReliableConsentAnchor) {
|
|
42166
42351
|
return autoApproveActive;
|
|
42167
42352
|
}
|
|
42168
42353
|
const modalSignature = [
|
|
@@ -42198,6 +42383,7 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42198
42383
|
this.pendingAutoApprovalSince = 0;
|
|
42199
42384
|
this.autoApproveInactiveSince = 0;
|
|
42200
42385
|
this.autoApproveMaskSince = 0;
|
|
42386
|
+
this.stalledApprovalNudgeEpisode = 0;
|
|
42201
42387
|
if (this.autoApproveBusyTimer) clearTimeout(this.autoApproveBusyTimer);
|
|
42202
42388
|
this.autoApproveBusyTimer = setTimeout(() => {
|
|
42203
42389
|
this.autoApproveBusy = false;
|
|
@@ -42325,6 +42511,18 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42325
42511
|
const previousStatus = this.lastStatus;
|
|
42326
42512
|
if (newStatus !== this.lastStatus) {
|
|
42327
42513
|
LOG.info("CLI", `[${this.type}] status: ${this.lastStatus} \u2192 ${newStatus}`);
|
|
42514
|
+
if (this.fsmTraceOn()) this.recordFsmTransitionTrace({
|
|
42515
|
+
from: this.lastStatus,
|
|
42516
|
+
to: newStatus,
|
|
42517
|
+
rawStatus,
|
|
42518
|
+
autoApproveActive,
|
|
42519
|
+
autoApproveHoldIdle,
|
|
42520
|
+
autoApproveBusy: this.autoApproveBusy,
|
|
42521
|
+
hasPending: this.hasAdapterPendingResponse(),
|
|
42522
|
+
busyEpoch: this.busyEpoch,
|
|
42523
|
+
lastOutputAt: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
|
|
42524
|
+
lastScreenChangeAt: typeof adapterStatus?.lastScreenChangeAt === "number" ? adapterStatus.lastScreenChangeAt : null
|
|
42525
|
+
});
|
|
42328
42526
|
const startingToGeneratingWithActiveTurn = this.lastStatus === "starting" && newStatus === "generating" && this.hasAdapterPendingResponse();
|
|
42329
42527
|
if (this.lastStatus === "idle" && newStatus === "generating" || startingToGeneratingWithActiveTurn) {
|
|
42330
42528
|
if (this.completedDebouncePending && this.generatingStartedAt === 0) {
|
|
@@ -42451,6 +42649,17 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42451
42649
|
if (this.isMeshWorkerSession()) {
|
|
42452
42650
|
traceMeshEventStage("arm", this.meshTraceCtx(), `short-generating settle-arm (source=${shortEvidenceSource}, missingEvidence=${missingEvidence})`);
|
|
42453
42651
|
}
|
|
42652
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
|
|
42653
|
+
branch: "short_generating",
|
|
42654
|
+
previousStatus: this.lastStatus,
|
|
42655
|
+
turnStartedAt: shortTurnStartedAt || null,
|
|
42656
|
+
busyEpochAtArm: this.busyEpoch,
|
|
42657
|
+
lastOutputAtArm: typeof adapterStatus?.lastOutputAt === "number" ? adapterStatus.lastOutputAt : null,
|
|
42658
|
+
flushDelay: NATIVE_HISTORY_MESH_IDLE_SETTLE_MS,
|
|
42659
|
+
evidenceSource: shortEvidenceSource,
|
|
42660
|
+
missingEvidence,
|
|
42661
|
+
hasFinalSummary: !!shortFinalSummary
|
|
42662
|
+
});
|
|
42454
42663
|
this.scheduleCompletedDebounceFlush(NATIVE_HISTORY_MESH_IDLE_SETTLE_MS);
|
|
42455
42664
|
} else if (missingEvidence) {
|
|
42456
42665
|
LOG.info("CLI", `[${this.type}] short completion suppressed: missing final assistant evidence, non-mesh session (source=${shortEvidenceSource})`);
|
|
@@ -42499,6 +42708,16 @@ var CliProviderInstance = class _CliProviderInstance {
|
|
|
42499
42708
|
const meshSettleSession = this.isAutonomousMeshSession();
|
|
42500
42709
|
const flushDelay = ownsExternalHistory ? meshSettleSession ? NATIVE_HISTORY_MESH_IDLE_SETTLE_MS : 0 : 3e3;
|
|
42501
42710
|
LOG.debug("CLI", `[${this.type}] set completedDebouncePending duration=${duration}s ownsExternalHistory=${ownsExternalHistory} meshSettle=${meshSettleSession} flushDelay=${flushDelay}ms generatingStartedAt=${this.generatingStartedAt}`);
|
|
42711
|
+
if (this.completionTraceOn()) this.recordCompletionGateTrace("arm", {
|
|
42712
|
+
branch: "normal",
|
|
42713
|
+
previousStatus: this.completedDebouncePending.previousStatus,
|
|
42714
|
+
turnStartedAt: this.completedDebouncePending.turnStartedAt ?? null,
|
|
42715
|
+
busyEpochAtArm: this.completedDebouncePending.busyEpochAtArm ?? null,
|
|
42716
|
+
lastOutputAtArm: this.completedDebouncePending.lastOutputAtArm ?? null,
|
|
42717
|
+
flushDelay,
|
|
42718
|
+
ownsExternalHistory,
|
|
42719
|
+
meshSettle: meshSettleSession
|
|
42720
|
+
});
|
|
42502
42721
|
this.scheduleCompletedDebounceFlush(flushDelay);
|
|
42503
42722
|
}
|
|
42504
42723
|
} else if (newStatus === "idle" && this.lastStatus === "starting") {
|
|
@@ -42751,7 +42970,8 @@ ${effect.notification.body || ""}`.trim();
|
|
|
42751
42970
|
return false;
|
|
42752
42971
|
}
|
|
42753
42972
|
/** @see ProviderInstance.noteManualInteraction */
|
|
42754
|
-
noteManualInteraction(now = Date.now()) {
|
|
42973
|
+
noteManualInteraction(now = Date.now(), opts) {
|
|
42974
|
+
if (opts?.passive && this.isMeshWorkerSession()) return;
|
|
42755
42975
|
this.manualAttendance.note(now);
|
|
42756
42976
|
}
|
|
42757
42977
|
/**
|
|
@@ -42775,6 +42995,50 @@ ${effect.notification.body || ""}`.trim();
|
|
|
42775
42995
|
autoApproveMaskStalled(now = Date.now()) {
|
|
42776
42996
|
return this.autoApproveMaskSince > 0 && now - this.autoApproveMaskSince > _CliProviderInstance.AUTO_APPROVE_MASK_STALL_MS;
|
|
42777
42997
|
}
|
|
42998
|
+
/**
|
|
42999
|
+
* NOTIF-APPROVAL-MASKED (Q1b): surface a delegated worker's STALLED auto-approve modal
|
|
43000
|
+
* to the mesh COORDINATOR, decoupled from the dashboard visible-status mask.
|
|
43001
|
+
*
|
|
43002
|
+
* When auto-approve is configured but the episode never settles (modal parse miss / the
|
|
43003
|
+
* settle gate never satisfied), getState()/detectStatusTransition() fold the raw
|
|
43004
|
+
* `waiting_approval` into `generating` to suppress dashboard flicker — so
|
|
43005
|
+
* detectStatusTransition()'s `waiting_approval` arm never runs and NO agent:waiting_approval
|
|
43006
|
+
* event is emitted. The coordinator's real-time approval-nudge delivery then has no input and
|
|
43007
|
+
* the worker's stuck modal is never surfaced (the live ~25s stall). The dashboard mask is
|
|
43008
|
+
* intentional and stays; this emits the coordinator nudge exactly ONCE, gated on the SAME
|
|
43009
|
+
* raw-waiting_approval + mask-stalled signal resolveModalParkStatus() distinguishes, the
|
|
43010
|
+
* instant the mask-stall threshold trips (the same moment getState un-folds the mask).
|
|
43011
|
+
*
|
|
43012
|
+
* Only delegated worker sessions qualify: a foreground session has no coordinator to notify,
|
|
43013
|
+
* and its own dashboard mask already reveals the modal on stall. A normally-resolving
|
|
43014
|
+
* auto-approve never reaches AUTO_APPROVE_MASK_STALL_MS, so it emits nothing here; and if a
|
|
43015
|
+
* masked approval clears just as this fires, rc.455's isApprovalNudgeResolved stale-drop
|
|
43016
|
+
* discards the nudge coordinator-side without noise. Dedup is per-episode (keyed on the
|
|
43017
|
+
* mask-clock value) so a modal that flaps between parsed/unparsed states is announced once.
|
|
43018
|
+
*/
|
|
43019
|
+
maybeEmitStalledApprovalNudge(adapterStatus, now) {
|
|
43020
|
+
if (!this.isMeshWorkerSession()) return;
|
|
43021
|
+
if (adapterStatus?.status !== "waiting_approval") return;
|
|
43022
|
+
if (!this.autoApproveMaskStalled(now)) return;
|
|
43023
|
+
if (this.stalledApprovalNudgeEpisode === this.autoApproveMaskSince) return;
|
|
43024
|
+
this.stalledApprovalNudgeEpisode = this.autoApproveMaskSince;
|
|
43025
|
+
const modal = adapterStatus.activeModal;
|
|
43026
|
+
const dirName = workingDirBasename(this.workingDir);
|
|
43027
|
+
const chatTitle = `${this.provider.name} \xB7 ${dirName}`;
|
|
43028
|
+
this.appendRuntimeSystemMessage(
|
|
43029
|
+
this.formatApprovalRequestMessage(modal?.message, modal?.buttons),
|
|
43030
|
+
`approval_request:${now}`,
|
|
43031
|
+
now
|
|
43032
|
+
);
|
|
43033
|
+
this.pushEvent({
|
|
43034
|
+
event: "agent:waiting_approval",
|
|
43035
|
+
chatTitle,
|
|
43036
|
+
timestamp: now,
|
|
43037
|
+
modalMessage: modal?.message,
|
|
43038
|
+
modalButtons: modal?.buttons
|
|
43039
|
+
});
|
|
43040
|
+
LOG.info("CLI", `[${this.type}] stalled auto-approve nudge \u2192 coordinator (masked ${Math.round((now - this.autoApproveMaskSince) / 1e3)}s)`);
|
|
43041
|
+
}
|
|
42778
43042
|
recordAutoApproval(modalMessage, buttonLabel, now = Date.now()) {
|
|
42779
43043
|
this.appendRuntimeSystemMessage(
|
|
42780
43044
|
formatAutoApprovalMessage(modalMessage, buttonLabel),
|
|
@@ -47458,12 +47722,17 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
47458
47722
|
logFn;
|
|
47459
47723
|
versionArchive = null;
|
|
47460
47724
|
scriptsCache = /* @__PURE__ */ new Map();
|
|
47725
|
+
/**
|
|
47726
|
+
* Resolved registry base URL and provider tarball URL. Resolution order:
|
|
47727
|
+
* explicit config field (constructor option) → env var → vendor default.
|
|
47728
|
+
* See `config/registry-resolver.ts`.
|
|
47729
|
+
*/
|
|
47730
|
+
registryBaseUrl;
|
|
47731
|
+
providerTarballUrl;
|
|
47461
47732
|
/** Inject VersionArchive so resolve() can auto-detect installed versions */
|
|
47462
47733
|
setVersionArchive(archive) {
|
|
47463
47734
|
this.versionArchive = archive;
|
|
47464
47735
|
}
|
|
47465
|
-
static GITHUB_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
|
|
47466
|
-
static REGISTRY_BASE_URL = "https://api.adhf.dev/api/v1/registry";
|
|
47467
47736
|
static META_FILE = ".meta.json";
|
|
47468
47737
|
static REGISTRY_META_FILE = ".registry-meta.json";
|
|
47469
47738
|
static REPO_PROVIDER_DIRNAME = "adhdev-providers";
|
|
@@ -47531,6 +47800,8 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
47531
47800
|
constructor(options) {
|
|
47532
47801
|
this.logFn = options?.logFn || LOG.forComponent("Provider").asLogFn();
|
|
47533
47802
|
this.probeStarts = options?.probeStarts ?? [process.cwd(), __dirname];
|
|
47803
|
+
this.registryBaseUrl = resolveRegistryBaseUrl(options?.registryUrl);
|
|
47804
|
+
this.providerTarballUrl = resolveProviderTarballUrl(options?.providerTarballUrl);
|
|
47534
47805
|
this.defaultProvidersDir = path35.join(os25.homedir(), ".adhdev", "providers");
|
|
47535
47806
|
const detected = this.detectDefaultUserDir();
|
|
47536
47807
|
this.userDir = detected.path;
|
|
@@ -48563,7 +48834,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48563
48834
|
this.log("Registry sync skipped (sourceMode=no-upstream)");
|
|
48564
48835
|
return { updated: false };
|
|
48565
48836
|
}
|
|
48566
|
-
this.log(`Registry sync starting (${
|
|
48837
|
+
this.log(`Registry sync starting (${this.registryBaseUrl})...`);
|
|
48567
48838
|
const https = __require("https");
|
|
48568
48839
|
const regMetaPath = path35.join(this.upstreamDir, _ProviderLoader.REGISTRY_META_FILE);
|
|
48569
48840
|
let cachedChecksums = {};
|
|
@@ -48574,7 +48845,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48574
48845
|
} catch {
|
|
48575
48846
|
}
|
|
48576
48847
|
try {
|
|
48577
|
-
const listUrl = `${
|
|
48848
|
+
const listUrl = `${this.registryBaseUrl}/providers`;
|
|
48578
48849
|
const listBody = await new Promise((resolve25, reject) => {
|
|
48579
48850
|
const req = https.get(listUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 1e4 }, (res) => {
|
|
48580
48851
|
if (res.statusCode !== 200) {
|
|
@@ -48598,7 +48869,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48598
48869
|
const { type, category, checksum, version } = entry;
|
|
48599
48870
|
const cacheKey = `${category}/${type}`;
|
|
48600
48871
|
if (cachedChecksums[cacheKey] === checksum) continue;
|
|
48601
|
-
const dlUrl = `${
|
|
48872
|
+
const dlUrl = `${this.registryBaseUrl}/providers/${type}/${version}/download`;
|
|
48602
48873
|
const manifestBody = await new Promise((resolve25, reject) => {
|
|
48603
48874
|
const req = https.get(dlUrl, { headers: { "User-Agent": "adhdev-daemon", "Accept": "application/json" }, timeout: 3e4 }, (res) => {
|
|
48604
48875
|
if (res.statusCode !== 200) {
|
|
@@ -48665,12 +48936,13 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48665
48936
|
this.log("Upstream check skipped (last check < 30min ago)");
|
|
48666
48937
|
return { updated: false };
|
|
48667
48938
|
}
|
|
48939
|
+
const tarballTarget = resolveProviderTarballTarget(this.providerTarballUrl);
|
|
48668
48940
|
try {
|
|
48669
48941
|
const etag = await new Promise((resolve25, reject) => {
|
|
48670
48942
|
const options = {
|
|
48671
48943
|
method: "HEAD",
|
|
48672
|
-
hostname:
|
|
48673
|
-
path:
|
|
48944
|
+
hostname: tarballTarget.hostname,
|
|
48945
|
+
path: tarballTarget.path,
|
|
48674
48946
|
headers: { "User-Agent": "adhdev-launcher" },
|
|
48675
48947
|
timeout: 1e4
|
|
48676
48948
|
};
|
|
@@ -48711,7 +48983,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48711
48983
|
this.log("Downloading latest providers from GitHub...");
|
|
48712
48984
|
const tmpTar = path35.join(os25.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
|
|
48713
48985
|
const tmpExtract = path35.join(os25.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
|
|
48714
|
-
await this.downloadFile(
|
|
48986
|
+
await this.downloadFile(tarballTarget.url, tmpTar);
|
|
48715
48987
|
fs25.mkdirSync(tmpExtract, { recursive: true });
|
|
48716
48988
|
await execAsync5(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
|
|
48717
48989
|
const extracted = fs25.readdirSync(tmpExtract);
|
|
@@ -48811,7 +49083,7 @@ var ProviderLoader = class _ProviderLoader {
|
|
|
48811
49083
|
etag,
|
|
48812
49084
|
timestamp,
|
|
48813
49085
|
lastCheck: new Date(timestamp).toISOString(),
|
|
48814
|
-
source:
|
|
49086
|
+
source: this.providerTarballUrl
|
|
48815
49087
|
}, null, 2));
|
|
48816
49088
|
} catch {
|
|
48817
49089
|
}
|
|
@@ -49923,6 +50195,47 @@ async function decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg) {
|
|
|
49923
50195
|
if (await isAncestor(worktreeOssSha, sourceSha)) return "advance";
|
|
49924
50196
|
return "skip_diverged";
|
|
49925
50197
|
}
|
|
50198
|
+
async function syncClonedWorktreeSubmodules(worktreePath, sourceWorkspace, rg) {
|
|
50199
|
+
const submodulePaths = getRegisteredSubmodulePaths(worktreePath);
|
|
50200
|
+
if (submodulePaths.size === 0) return;
|
|
50201
|
+
const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
|
|
50202
|
+
const worktreeCtx = { workspace: worktreePath, repoRoot: worktreePath, isGitRepo: true };
|
|
50203
|
+
const readStdout = (out) => (typeof out === "string" ? out : out?.stdout ?? "").trim();
|
|
50204
|
+
for (const submodulePath of submodulePaths) {
|
|
50205
|
+
try {
|
|
50206
|
+
const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", submodulePath], { timeoutMs: 1e4 });
|
|
50207
|
+
const sourceSha = readStdout(sourceStatusOut).match(/^[+\- ]?([0-9a-f]{40})/)?.[1];
|
|
50208
|
+
if (!sourceSha) continue;
|
|
50209
|
+
const subCtx = {
|
|
50210
|
+
workspace: `${worktreePath}/${submodulePath}`,
|
|
50211
|
+
repoRoot: `${worktreePath}/${submodulePath}`,
|
|
50212
|
+
isGitRepo: true
|
|
50213
|
+
};
|
|
50214
|
+
const worktreeSubSha = readStdout(await rg(subCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 }));
|
|
50215
|
+
if (!worktreeSubSha || worktreeSubSha === sourceSha) continue;
|
|
50216
|
+
await rg(subCtx, ["fetch", `${sourceWorkspace}/${submodulePath}`, "HEAD"], { timeoutMs: 6e4 });
|
|
50217
|
+
let action;
|
|
50218
|
+
try {
|
|
50219
|
+
action = await decideOssCloneSync(subCtx, worktreeSubSha, sourceSha, rg);
|
|
50220
|
+
} catch (decideErr) {
|
|
50221
|
+
action = "skip_diverged";
|
|
50222
|
+
console.warn(`[mesh] ${submodulePath} submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
|
|
50223
|
+
}
|
|
50224
|
+
if (action === "advance") {
|
|
50225
|
+
await rg(subCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
|
|
50226
|
+
await rg(worktreeCtx, ["add", submodulePath], { timeoutMs: 1e4 });
|
|
50227
|
+
await rg(worktreeCtx, ["commit", "-m", `chore: sync ${submodulePath} to source node HEAD on clone`], { timeoutMs: 1e4 });
|
|
50228
|
+
console.log(`[mesh] Advanced ${submodulePath} submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
|
|
50229
|
+
} else if (action === "skip_rewind") {
|
|
50230
|
+
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`);
|
|
50231
|
+
} else if (action === "skip_diverged") {
|
|
50232
|
+
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)`);
|
|
50233
|
+
}
|
|
50234
|
+
} catch (subErr) {
|
|
50235
|
+
console.warn(`[mesh] ${submodulePath} submodule sync to source HEAD failed (best-effort):`, subErr?.message ?? subErr);
|
|
50236
|
+
}
|
|
50237
|
+
}
|
|
50238
|
+
}
|
|
49926
50239
|
var meshCrudHandlers = {
|
|
49927
50240
|
list_meshes: async (ctx, _args) => {
|
|
49928
50241
|
try {
|
|
@@ -50627,42 +50940,8 @@ var meshCrudHandlers = {
|
|
|
50627
50940
|
submodulesInitialized2 = true;
|
|
50628
50941
|
const sourceWorkspace = sourceNode.repoRoot || sourceNode.workspace;
|
|
50629
50942
|
if (sourceWorkspace) {
|
|
50630
|
-
|
|
50631
|
-
|
|
50632
|
-
const sourceCtx = { workspace: sourceWorkspace, repoRoot: sourceWorkspace, isGitRepo: true };
|
|
50633
|
-
const worktreeCtx = { workspace: result.worktreePath, repoRoot: result.worktreePath, isGitRepo: true };
|
|
50634
|
-
const sourceStatusOut = await rg(sourceCtx, ["submodule", "status", "oss"], { timeoutMs: 1e4 });
|
|
50635
|
-
const sourceStatusLine = (typeof sourceStatusOut === "string" ? sourceStatusOut : sourceStatusOut?.stdout ?? "").trim();
|
|
50636
|
-
const sourceShaMatch = sourceStatusLine.match(/^[+\- ]?([0-9a-f]{40})/);
|
|
50637
|
-
const sourceSha = sourceShaMatch?.[1];
|
|
50638
|
-
if (sourceSha) {
|
|
50639
|
-
const ossCtx = { workspace: `${result.worktreePath}/oss`, repoRoot: `${result.worktreePath}/oss`, isGitRepo: true };
|
|
50640
|
-
const worktreeOssHeadOut = await rg(ossCtx, ["rev-parse", "HEAD"], { timeoutMs: 1e4 });
|
|
50641
|
-
const worktreeOssSha = (typeof worktreeOssHeadOut === "string" ? worktreeOssHeadOut : worktreeOssHeadOut?.stdout ?? "").trim();
|
|
50642
|
-
if (worktreeOssSha && worktreeOssSha !== sourceSha) {
|
|
50643
|
-
await rg(ossCtx, ["fetch", `${sourceWorkspace}/oss`, "HEAD"], { timeoutMs: 6e4 });
|
|
50644
|
-
let ossAction;
|
|
50645
|
-
try {
|
|
50646
|
-
ossAction = await decideOssCloneSync(ossCtx, worktreeOssSha, sourceSha, rg);
|
|
50647
|
-
} catch (decideErr) {
|
|
50648
|
-
ossAction = "skip_diverged";
|
|
50649
|
-
console.warn(`[mesh] oss submodule sync guard could not resolve ancestry (kept fresh worktree HEAD): ${decideErr?.message ?? decideErr}`);
|
|
50650
|
-
}
|
|
50651
|
-
if (ossAction === "advance") {
|
|
50652
|
-
await rg(ossCtx, ["checkout", sourceSha], { timeoutMs: 1e4 });
|
|
50653
|
-
await rg(worktreeCtx, ["add", "oss"], { timeoutMs: 1e4 });
|
|
50654
|
-
await rg(worktreeCtx, ["commit", "-m", "chore: sync oss to source node HEAD on clone"], { timeoutMs: 1e4 });
|
|
50655
|
-
console.log(`[mesh] Advanced oss submodule to newer source HEAD ${sourceSha.slice(0, 8)} in worktree`);
|
|
50656
|
-
} else if (ossAction === "skip_rewind") {
|
|
50657
|
-
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`);
|
|
50658
|
-
} else if (ossAction === "skip_diverged") {
|
|
50659
|
-
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)`);
|
|
50660
|
-
}
|
|
50661
|
-
}
|
|
50662
|
-
}
|
|
50663
|
-
} catch (ossErr) {
|
|
50664
|
-
console.warn("[mesh] oss submodule sync to source HEAD failed (best-effort):", ossErr.message);
|
|
50665
|
-
}
|
|
50943
|
+
const { runGit: rg } = await Promise.resolve().then(() => (init_git_executor(), git_executor_exports));
|
|
50944
|
+
await syncClonedWorktreeSubmodules(result.worktreePath, sourceWorkspace, rg);
|
|
50666
50945
|
}
|
|
50667
50946
|
} catch (subErr) {
|
|
50668
50947
|
console.warn("[mesh] Submodule init failed for worktree:", subErr.message);
|
|
@@ -52096,6 +52375,26 @@ import { execFileSync as execFileSync6 } from "child_process";
|
|
|
52096
52375
|
import { existsSync as existsSync40, readFileSync as readFileSync31 } from "fs";
|
|
52097
52376
|
import { resolve as resolve19 } from "path";
|
|
52098
52377
|
var PREVIEW_DEPLOY_RECORD = ".adhdev/preview-deploy.json";
|
|
52378
|
+
var PREVIEW_PIPELINE_SCRIPTS = [
|
|
52379
|
+
"scripts/preview-freshness.mjs",
|
|
52380
|
+
"scripts/smoke-preview-web.mjs",
|
|
52381
|
+
"scripts/deploy-preview-local.mjs"
|
|
52382
|
+
];
|
|
52383
|
+
function hasDeployPreviewNpmScript(repoRoot) {
|
|
52384
|
+
const pkgPath = resolve19(repoRoot, "package.json");
|
|
52385
|
+
if (!existsSync40(pkgPath)) return false;
|
|
52386
|
+
try {
|
|
52387
|
+
const pkg = JSON.parse(readFileSync31(pkgPath, "utf8"));
|
|
52388
|
+
return typeof pkg?.scripts?.["deploy:preview"] === "string";
|
|
52389
|
+
} catch {
|
|
52390
|
+
return false;
|
|
52391
|
+
}
|
|
52392
|
+
}
|
|
52393
|
+
function isPreviewPipelineConfigured(repoRoot) {
|
|
52394
|
+
if (existsSync40(resolve19(repoRoot, PREVIEW_DEPLOY_RECORD))) return true;
|
|
52395
|
+
if (PREVIEW_PIPELINE_SCRIPTS.some((rel) => existsSync40(resolve19(repoRoot, rel)))) return true;
|
|
52396
|
+
return hasDeployPreviewNpmScript(repoRoot);
|
|
52397
|
+
}
|
|
52099
52398
|
function runGit2(repoRoot, args) {
|
|
52100
52399
|
try {
|
|
52101
52400
|
return execFileSync6("git", args, {
|
|
@@ -52147,6 +52446,7 @@ function readCurrentMainCommit(repoRoot) {
|
|
|
52147
52446
|
return { currentMainCommit: null, currentMainCommitSource: "unknown" };
|
|
52148
52447
|
}
|
|
52149
52448
|
function buildPreviewFreshness(repoRoot) {
|
|
52449
|
+
if (!isPreviewPipelineConfigured(repoRoot)) return null;
|
|
52150
52450
|
const current = readCurrentMainCommit(repoRoot);
|
|
52151
52451
|
const record = readRecord6(repoRoot);
|
|
52152
52452
|
const lastPreviewCommit = normalizeCommit(record?.lastPreviewCommit);
|
|
@@ -65867,7 +66167,9 @@ async function initDaemonComponents(config) {
|
|
|
65867
66167
|
const providerLoader = new ProviderLoader({
|
|
65868
66168
|
logFn: config.providerLogFn,
|
|
65869
66169
|
sourceMode: providerSourceMode,
|
|
65870
|
-
userDir: appConfig.providerDir
|
|
66170
|
+
userDir: appConfig.providerDir,
|
|
66171
|
+
registryUrl: appConfig.registryUrl,
|
|
66172
|
+
providerTarballUrl: appConfig.providerTarballUrl
|
|
65871
66173
|
});
|
|
65872
66174
|
providerLoader.loadAll();
|
|
65873
66175
|
providerLoader.registerToDetector();
|