@neurcode-ai/cli 0.19.8 โ 0.20.1
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/api-client.d.ts +4 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js.map +1 -1
- package/dist/commands/activate.d.ts +6 -0
- package/dist/commands/activate.d.ts.map +1 -1
- package/dist/commands/activate.js +24 -0
- package/dist/commands/activate.js.map +1 -1
- package/dist/commands/brain.d.ts.map +1 -1
- package/dist/commands/brain.js +114 -17
- package/dist/commands/brain.js.map +1 -1
- package/dist/commands/runtime-doctor.d.ts.map +1 -1
- package/dist/commands/runtime-doctor.js +41 -2
- package/dist/commands/runtime-doctor.js.map +1 -1
- package/dist/commands/runtime-identity.d.ts.map +1 -1
- package/dist/commands/runtime-identity.js +46 -1
- package/dist/commands/runtime-identity.js.map +1 -1
- package/dist/commands/runtime-sync.d.ts.map +1 -1
- package/dist/commands/runtime-sync.js +55 -0
- package/dist/commands/runtime-sync.js.map +1 -1
- package/dist/commands/session-hook.d.ts +16 -1
- package/dist/commands/session-hook.d.ts.map +1 -1
- package/dist/commands/session-hook.js +165 -10
- package/dist/commands/session-hook.js.map +1 -1
- package/dist/commands/session.d.ts +4 -1
- package/dist/commands/session.d.ts.map +1 -1
- package/dist/commands/session.js +39 -17
- package/dist/commands/session.js.map +1 -1
- package/dist/index.js +36 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime-build.json +5 -5
- package/dist/utils/RelevanceScorer.d.ts.map +1 -1
- package/dist/utils/RelevanceScorer.js +16 -9
- package/dist/utils/RelevanceScorer.js.map +1 -1
- package/dist/utils/agent-session-launcher.d.ts.map +1 -1
- package/dist/utils/agent-session-launcher.js +142 -97
- package/dist/utils/agent-session-launcher.js.map +1 -1
- package/dist/utils/brain-lifecycle.d.ts +62 -0
- package/dist/utils/brain-lifecycle.d.ts.map +1 -0
- package/dist/utils/brain-lifecycle.js +482 -0
- package/dist/utils/brain-lifecycle.js.map +1 -0
- package/dist/utils/cli-startup.d.ts.map +1 -1
- package/dist/utils/cli-startup.js +20 -0
- package/dist/utils/cli-startup.js.map +1 -1
- package/dist/utils/command-budget.d.ts +10 -0
- package/dist/utils/command-budget.d.ts.map +1 -0
- package/dist/utils/command-budget.js +203 -0
- package/dist/utils/command-budget.js.map +1 -0
- package/dist/utils/mcp-server-pin.d.ts +1 -1
- package/dist/utils/mcp-server-pin.js +2 -2
- package/dist/utils/runtime-authority.d.ts +27 -0
- package/dist/utils/runtime-authority.d.ts.map +1 -0
- package/dist/utils/runtime-authority.js +162 -0
- package/dist/utils/runtime-authority.js.map +1 -0
- package/dist/utils/runtime-companion.d.ts +32 -0
- package/dist/utils/runtime-companion.d.ts.map +1 -1
- package/dist/utils/runtime-companion.js +57 -2
- package/dist/utils/runtime-companion.js.map +1 -1
- package/dist/utils/runtime-live.d.ts.map +1 -1
- package/dist/utils/runtime-live.js +14 -6
- package/dist/utils/runtime-live.js.map +1 -1
- package/dist/utils/runtime-outbox.d.ts.map +1 -1
- package/dist/utils/runtime-outbox.js +133 -1
- package/dist/utils/runtime-outbox.js.map +1 -1
- package/dist/utils/runtime-privacy.d.ts.map +1 -1
- package/dist/utils/runtime-privacy.js +52 -0
- package/dist/utils/runtime-privacy.js.map +1 -1
- package/dist/utils/runtime-state.d.ts +7 -35
- package/dist/utils/runtime-state.d.ts.map +1 -1
- package/dist/utils/runtime-state.js +203 -134
- package/dist/utils/runtime-state.js.map +1 -1
- package/dist/utils/session-start-transaction.d.ts +31 -0
- package/dist/utils/session-start-transaction.d.ts.map +1 -0
- package/dist/utils/session-start-transaction.js +207 -0
- package/dist/utils/session-start-transaction.js.map +1 -0
- package/dist/utils/v0-governance.d.ts +2 -1
- package/dist/utils/v0-governance.d.ts.map +1 -1
- package/dist/utils/v0-governance.js +271 -11
- package/dist/utils/v0-governance.js.map +1 -1
- package/package.json +6 -4
|
@@ -23,6 +23,7 @@ exports.resolveSessionForHook = resolveSessionForHook;
|
|
|
23
23
|
exports.normalizeHookFilePathForRepo = normalizeHookFilePathForRepo;
|
|
24
24
|
exports.hookFilePathCandidates = hookFilePathCandidates;
|
|
25
25
|
exports.proposedSourceFromHookInput = proposedSourceFromHookInput;
|
|
26
|
+
exports.governanceWasExpected = governanceWasExpected;
|
|
26
27
|
exports.evaluateNoActiveSessionWrite = evaluateNoActiveSessionWrite;
|
|
27
28
|
exports.shouldKeepSessionActiveForPendingApproval = shouldKeepSessionActiveForPendingApproval;
|
|
28
29
|
exports.reconcileTrustedAdapterPosture = reconcileTrustedAdapterPosture;
|
|
@@ -46,10 +47,14 @@ const structural_understanding_1 = require("../utils/structural-understanding");
|
|
|
46
47
|
const consequence_nudges_1 = require("../utils/consequence-nudges");
|
|
47
48
|
const agent_guard_supervisor_1 = require("../utils/agent-guard-supervisor");
|
|
48
49
|
const local_repo_brain_1 = require("../utils/local-repo-brain");
|
|
50
|
+
const runtime_authority_1 = require("../utils/runtime-authority");
|
|
51
|
+
const brain_lifecycle_1 = require("../utils/brain-lifecycle");
|
|
49
52
|
const proposed_change_analysis_1 = require("../utils/proposed-change-analysis");
|
|
50
53
|
const repo_intelligence_v2_1 = require("../utils/repo-intelligence-v2");
|
|
51
54
|
const runtime_companion_1 = require("../utils/runtime-companion");
|
|
52
55
|
const profile_drift_recovery_1 = require("../utils/profile-drift-recovery");
|
|
56
|
+
const session_start_transaction_1 = require("../utils/session-start-transaction");
|
|
57
|
+
const runtime_state_1 = require("../utils/runtime-state");
|
|
53
58
|
// โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
54
59
|
/** Read the full hook JSON from stdin, or return {} on any error. */
|
|
55
60
|
function readHookInput() {
|
|
@@ -584,8 +589,62 @@ function blockContext(input) {
|
|
|
584
589
|
};
|
|
585
590
|
}
|
|
586
591
|
const NO_ACTIVE_SESSION_SCOPE_SENTINEL = '__neurcode_no_active_session_scope__';
|
|
592
|
+
/**
|
|
593
|
+
* Whether governance was previously established for this repository โ a built profile, an
|
|
594
|
+
* active-session pointer, or any persisted session record. Used to distinguish ordinary
|
|
595
|
+
* first-run use (advisory) from a runtime that was expected to govern but is currently
|
|
596
|
+
* unavailable (fail closed). Dynamic โ derived from on-disk governance artifacts only, no
|
|
597
|
+
* hardcoded repository directory names.
|
|
598
|
+
*/
|
|
599
|
+
function governanceWasExpected(repoRoot) {
|
|
600
|
+
try {
|
|
601
|
+
if ((0, fs_1.existsSync)((0, v0_governance_1.profilePath)(repoRoot)))
|
|
602
|
+
return true;
|
|
603
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(repoRoot, '.neurcode', 'active-session.json')))
|
|
604
|
+
return true;
|
|
605
|
+
const sessions = (0, path_1.join)(repoRoot, '.neurcode', 'sessions');
|
|
606
|
+
return (0, fs_1.existsSync)(sessions) && (0, fs_1.readdirSync)(sessions).length > 0;
|
|
607
|
+
}
|
|
608
|
+
catch {
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function emptyNoSessionBoundary(filePath) {
|
|
613
|
+
return (0, governance_runtime_1.checkFileBoundary)({
|
|
614
|
+
filePath,
|
|
615
|
+
allowedGlobs: [NO_ACTIVE_SESSION_SCOPE_SENTINEL],
|
|
616
|
+
ownershipRules: [],
|
|
617
|
+
sensitiveGlobs: [],
|
|
618
|
+
approvalRequiredGlobs: [],
|
|
619
|
+
approvedPaths: [],
|
|
620
|
+
approvalGrants: [],
|
|
621
|
+
scopeMode: 'explicit',
|
|
622
|
+
localMode: 'strict',
|
|
623
|
+
});
|
|
624
|
+
}
|
|
587
625
|
function evaluateNoActiveSessionWrite(repoRoot, filePath) {
|
|
588
|
-
|
|
626
|
+
let profile;
|
|
627
|
+
try {
|
|
628
|
+
profile = (0, v0_governance_1.ensureFreshGovernanceProfile)(repoRoot).profile;
|
|
629
|
+
}
|
|
630
|
+
catch {
|
|
631
|
+
// P0-E: the governance runtime is expected (this hook is installed) but the profile
|
|
632
|
+
// cannot be evaluated. If governance was previously established, FAIL CLOSED โ a
|
|
633
|
+
// protected path must never pass merely because session creation / the runtime failed.
|
|
634
|
+
// First-run repos (no prior governance) stay advisory to avoid blocking ordinary use.
|
|
635
|
+
const assessment = (0, runtime_state_1.classifyRuntimeState)(repoRoot);
|
|
636
|
+
const expected = assessment.governanceExpected;
|
|
637
|
+
return {
|
|
638
|
+
block: expected,
|
|
639
|
+
filePath,
|
|
640
|
+
result: emptyNoSessionBoundary(filePath),
|
|
641
|
+
runtimeState: expected ? assessment.state : 'installed_not_activated',
|
|
642
|
+
message: expected
|
|
643
|
+
? `โธ Neurcode: the governance runtime is unavailable and cannot verify whether ${filePath} is a protected path, but governance is expected for this repository. Failing closed. Run exactly: ${assessment.recoveryCommand}.`
|
|
644
|
+
: `No governance profile yet at ${repoRoot}; ${filePath} is allowed advisory-only until a governed session establishes one.`,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
const assessment = (0, runtime_state_1.classifyRuntimeState)(repoRoot);
|
|
589
648
|
const result = (0, governance_runtime_1.checkFileBoundary)({
|
|
590
649
|
filePath,
|
|
591
650
|
allowedGlobs: [NO_ACTIVE_SESSION_SCOPE_SENTINEL],
|
|
@@ -598,14 +657,19 @@ function evaluateNoActiveSessionWrite(repoRoot, filePath) {
|
|
|
598
657
|
localMode: 'strict',
|
|
599
658
|
});
|
|
600
659
|
const protectedPath = result.isApprovalRequired || result.isSensitive || result.owners.length > 0;
|
|
660
|
+
const enforcementPaused = assessment.state === 'enforcement_paused';
|
|
601
661
|
const ownerNote = result.owners.length ? ` Owners: ${result.owners.join(', ')}.` : '';
|
|
602
|
-
const message =
|
|
603
|
-
?
|
|
604
|
-
:
|
|
662
|
+
const message = enforcementPaused
|
|
663
|
+
? `Neurcode enforcement is intentionally paused; ${filePath} is advisory-only. Resume with exactly: ${assessment.recoveryCommand}.`
|
|
664
|
+
: protectedPath
|
|
665
|
+
? `โธ Neurcode: no active governed session is running, so protected path ${filePath} cannot be checked or approved safely.${ownerNote} Start a governed session with \`neurcode session-hook start\`/agent activation, or run \`neurcode doctor --runtime\` for recovery before retrying.`
|
|
666
|
+
: `No active governed session at ${repoRoot}; ${filePath} is not a detected protected path and is allowed advisory-only.`;
|
|
605
667
|
return {
|
|
606
|
-
block: protectedPath,
|
|
668
|
+
block: protectedPath && !enforcementPaused,
|
|
607
669
|
filePath,
|
|
608
670
|
result,
|
|
671
|
+
// Profile is readable but no session is active: governance was expected to run here.
|
|
672
|
+
runtimeState: assessment.state,
|
|
609
673
|
message,
|
|
610
674
|
};
|
|
611
675
|
}
|
|
@@ -953,7 +1017,12 @@ async function handleStart(cmdCwd) {
|
|
|
953
1017
|
// No text in the prompt โ skip session creation (tool-use-only turn)
|
|
954
1018
|
return;
|
|
955
1019
|
}
|
|
1020
|
+
(0, session_start_transaction_1.beginSessionStartTransaction)(repoRoot, process.env.NEURCODE_BOUNDED_COMMAND_KEY || 'session_hook_start');
|
|
1021
|
+
// Hoisted so the catch can roll back a session that was created but never activated.
|
|
1022
|
+
let session = null;
|
|
1023
|
+
let sessionActivated = false;
|
|
956
1024
|
try {
|
|
1025
|
+
(0, session_start_transaction_1.updateSessionStartTransaction)(repoRoot, { phase: 'fingerprinting_profile' });
|
|
957
1026
|
const profileResult = (0, v0_governance_1.ensureFreshGovernanceProfile)(repoRoot);
|
|
958
1027
|
let profileFreshness = (0, v0_governance_1.buildProfileFreshnessSignal)(profileResult, profileResult.refreshed ? 'auto_refreshed' : 'none');
|
|
959
1028
|
if (profileResult.refreshed && profileResult.status !== 'missing') {
|
|
@@ -1085,7 +1154,16 @@ async function handleStart(cmdCwd) {
|
|
|
1085
1154
|
return;
|
|
1086
1155
|
}
|
|
1087
1156
|
const profile = profileResult.profile;
|
|
1088
|
-
|
|
1157
|
+
// Transactional start (P0-D): create the durable session record WITHOUT publishing
|
|
1158
|
+
// the active pointer. The pointer is published (activated) only after every
|
|
1159
|
+
// session-shaping step below succeeds, so a start that fails partway leaves no active
|
|
1160
|
+
// pointer and no partial session โ it is rolled back in the catch.
|
|
1161
|
+
(0, session_start_transaction_1.updateSessionStartTransaction)(repoRoot, { phase: 'persisting_deferred_session' });
|
|
1162
|
+
session = (0, governance_runtime_1.createSession)(repoRoot, profile, goal.trim(), { activate: false });
|
|
1163
|
+
(0, session_start_transaction_1.updateSessionStartTransaction)(repoRoot, {
|
|
1164
|
+
phase: 'shaping_session',
|
|
1165
|
+
sessionId: session.sessionId,
|
|
1166
|
+
});
|
|
1089
1167
|
const plannedAtStart = maybeCaptureAgentPlan(repoRoot, session, hookInput);
|
|
1090
1168
|
if (plannedAtStart)
|
|
1091
1169
|
session = plannedAtStart;
|
|
@@ -1127,13 +1205,36 @@ async function handleStart(cmdCwd) {
|
|
|
1127
1205
|
: '');
|
|
1128
1206
|
const banner = `๐ Neurcode session ${session.sessionId} ยท ${scopeNote} ยท ` +
|
|
1129
1207
|
`${session.contract.approvalRequiredGlobs.length} approval-required boundaries`;
|
|
1208
|
+
// Commit: all session-shaping steps succeeded, so publish the active pointer now.
|
|
1209
|
+
(0, session_start_transaction_1.updateSessionStartTransaction)(repoRoot, {
|
|
1210
|
+
phase: 'activating_session',
|
|
1211
|
+
sessionId: session.sessionId,
|
|
1212
|
+
});
|
|
1213
|
+
(0, governance_runtime_1.activateSession)(repoRoot, session.sessionId);
|
|
1214
|
+
sessionActivated = true;
|
|
1130
1215
|
process.stdout.write(JSON.stringify({ message: banner }) + '\n');
|
|
1216
|
+
// Cloud projection is non-authoritative and must not affect the committed session.
|
|
1217
|
+
(0, session_start_transaction_1.updateSessionStartTransaction)(repoRoot, {
|
|
1218
|
+
phase: 'reconciling_cloud',
|
|
1219
|
+
sessionId: session.sessionId,
|
|
1220
|
+
});
|
|
1131
1221
|
await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session, { profileFreshness });
|
|
1132
1222
|
}
|
|
1133
1223
|
catch (err) {
|
|
1224
|
+
// A start that failed before activation is rolled back so no partial session and no
|
|
1225
|
+
// dangling active pointer survive (P0-D: failed start leaves nothing behind).
|
|
1226
|
+
if (session && !sessionActivated) {
|
|
1227
|
+
try {
|
|
1228
|
+
(0, governance_runtime_1.removeSession)(repoRoot, session.sessionId);
|
|
1229
|
+
}
|
|
1230
|
+
catch { /* best effort rollback */ }
|
|
1231
|
+
}
|
|
1134
1232
|
diagnostic(`start failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1135
1233
|
// Fail open โ don't break the agent turn
|
|
1136
1234
|
}
|
|
1235
|
+
finally {
|
|
1236
|
+
(0, session_start_transaction_1.clearSessionStartTransaction)(repoRoot);
|
|
1237
|
+
}
|
|
1137
1238
|
}
|
|
1138
1239
|
const HARD_PREWRITE_ADAPTERS = new Set(['claude-code-hooks', 'copilot-hooks']);
|
|
1139
1240
|
/**
|
|
@@ -1157,6 +1258,20 @@ async function handleCheck(cmdCwd, trustedAdapterId, trustedTiming) {
|
|
|
1157
1258
|
const hookInput = readHookInput();
|
|
1158
1259
|
const effectiveCwd = cwdFromHookInput(hookInput, cmdCwd);
|
|
1159
1260
|
const repoRoot = (0, v0_governance_1.resolveRepoRoot)(effectiveCwd);
|
|
1261
|
+
try {
|
|
1262
|
+
(0, runtime_authority_1.assertProtectedRuntimeAuthority)(repoRoot, trustedAdapterId);
|
|
1263
|
+
}
|
|
1264
|
+
catch (error) {
|
|
1265
|
+
denyPreToolUse(error instanceof Error ? error.message : String(error), {
|
|
1266
|
+
blockContext: blockContext({
|
|
1267
|
+
blockType: 'profile_or_runtime_health_block',
|
|
1268
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1269
|
+
runtimeMode: 'strict',
|
|
1270
|
+
nextAction: 'Run `neurcode runtime repair`, restart the agent integration if requested, and retry.',
|
|
1271
|
+
}),
|
|
1272
|
+
});
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1160
1275
|
(0, hook_heartbeat_1.recordHookHeartbeat)({ repoRoot, eventType: 'check' });
|
|
1161
1276
|
const requestedSessionId = sessionIdFromHookInput(hookInput);
|
|
1162
1277
|
const toolName = hookInput['tool_name'] ||
|
|
@@ -1199,6 +1314,19 @@ async function handleCheck(cmdCwd, trustedAdapterId, trustedTiming) {
|
|
|
1199
1314
|
}
|
|
1200
1315
|
}
|
|
1201
1316
|
catch (error) {
|
|
1317
|
+
// P0-E defense in depth: if the protected-path check itself errors and governance
|
|
1318
|
+
// was expected for this repo, fail closed for this path rather than allow it.
|
|
1319
|
+
if (governanceWasExpected(repoRoot)) {
|
|
1320
|
+
denyPreToolUse(`โธ Neurcode could not verify whether ${filePath} is a protected path because the governance runtime errored, and governance is expected for this repository. Failing closed. Run \`neurcode runtime repair\`, then start a governed session and retry.`, {
|
|
1321
|
+
blockContext: blockContext({
|
|
1322
|
+
blockType: 'profile_or_runtime_health_block',
|
|
1323
|
+
filePath,
|
|
1324
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1325
|
+
runtimeMode: 'strict',
|
|
1326
|
+
nextAction: 'Run `neurcode runtime repair`, then start a governed session and retry this path.',
|
|
1327
|
+
}),
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1202
1330
|
diagnostic(`no-active-session protected-path check skipped: ${error instanceof Error ? error.message : String(error)}`);
|
|
1203
1331
|
}
|
|
1204
1332
|
}
|
|
@@ -1974,6 +2102,13 @@ async function handleFinish(cmdCwd) {
|
|
|
1974
2102
|
if (supervisorStop.signaled) {
|
|
1975
2103
|
diagnostic(`agent guard supervisor stop requested (pid ${supervisorStop.state?.pid ?? 'unknown'})`);
|
|
1976
2104
|
}
|
|
2105
|
+
try {
|
|
2106
|
+
const brain = await (0, brain_lifecycle_1.scheduleBrainIndex)(repoRoot, { force: true });
|
|
2107
|
+
diagnostic(`repository Brain refresh ${brain.state}`);
|
|
2108
|
+
}
|
|
2109
|
+
catch (brainError) {
|
|
2110
|
+
diagnostic(`repository Brain refresh scheduling failed: ${brainError instanceof Error ? brainError.message : String(brainError)}`);
|
|
2111
|
+
}
|
|
1977
2112
|
const blockCount = finished.events.filter((e) => e.type === 'check_block').length;
|
|
1978
2113
|
const warnCount = finished.events.filter((e) => e.type === 'check_warn').length;
|
|
1979
2114
|
const unresolvedLine = pendingActionableBlock
|
|
@@ -2072,6 +2207,8 @@ function sessionHookCommand(program) {
|
|
|
2072
2207
|
const cwd = opts.dir || process.cwd();
|
|
2073
2208
|
const repoRoot = (0, v0_governance_1.resolveRepoRoot)(cwd);
|
|
2074
2209
|
try {
|
|
2210
|
+
// 1) Authoritative, durable local approval. If this throws, NOTHING is approved
|
|
2211
|
+
// and the outer catch reports a real failure (exit 1, no "Approved" output).
|
|
2075
2212
|
const result = (0, governance_runtime_1.approveSession)(repoRoot, subOpts.path, {
|
|
2076
2213
|
reason: subOpts.reason,
|
|
2077
2214
|
sessionId: subOpts.sessionId,
|
|
@@ -2079,8 +2216,26 @@ function sessionHookCommand(program) {
|
|
|
2079
2216
|
ttlMs: subOpts.expiry === false || subOpts.expiresAt ? undefined : parseDurationMs(subOpts.expiresIn),
|
|
2080
2217
|
source: 'local_cli',
|
|
2081
2218
|
});
|
|
2219
|
+
// 2) Cloud reconcile is NON-AUTHORITATIVE. A reconcile failure must never be
|
|
2220
|
+
// reported as a failed approval (Apache Airflow dogfood P0-C: the misleading
|
|
2221
|
+
// "โ
Approved โฆ approval failed" sequence). Surface it as a distinct,
|
|
2222
|
+
// non-fatal status and keep exit 0 โ the local approval is already durable.
|
|
2223
|
+
let reconcile = { ok: true };
|
|
2224
|
+
try {
|
|
2225
|
+
const session = (0, governance_runtime_1.loadSession)(repoRoot, result.sessionId);
|
|
2226
|
+
if (session) {
|
|
2227
|
+
const published = await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session);
|
|
2228
|
+
reconcile = { ok: published.ok, error: published.error, pending: published.pending };
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
catch (reconcileErr) {
|
|
2232
|
+
reconcile = {
|
|
2233
|
+
ok: false,
|
|
2234
|
+
error: reconcileErr instanceof Error ? reconcileErr.message : String(reconcileErr),
|
|
2235
|
+
};
|
|
2236
|
+
}
|
|
2082
2237
|
if (subOpts.json) {
|
|
2083
|
-
process.stdout.write(JSON.stringify({ ok: true, ...result }, null, 2) + '\n');
|
|
2238
|
+
process.stdout.write(JSON.stringify({ ok: true, ...result, cloudReconcile: reconcile }, null, 2) + '\n');
|
|
2084
2239
|
}
|
|
2085
2240
|
else {
|
|
2086
2241
|
process.stdout.write([
|
|
@@ -2089,10 +2244,10 @@ function sessionHookCommand(program) {
|
|
|
2089
2244
|
` Expires: ${result.expiresAt || 'session end'}`,
|
|
2090
2245
|
` All approved paths: ${result.approvedPaths.join(', ')}`,
|
|
2091
2246
|
].join('\n') + '\n');
|
|
2247
|
+
if (!reconcile.ok) {
|
|
2248
|
+
process.stderr.write(`[neurcode] note: approval is durable locally; cloud reconcile deferred${reconcile.error ? ` (${reconcile.error})` : ''}\n`);
|
|
2249
|
+
}
|
|
2092
2250
|
}
|
|
2093
|
-
const session = (0, governance_runtime_1.loadSession)(repoRoot, result.sessionId);
|
|
2094
|
-
if (session)
|
|
2095
|
-
await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session);
|
|
2096
2251
|
}
|
|
2097
2252
|
catch (err) {
|
|
2098
2253
|
const msg = err instanceof Error ? err.message : String(err);
|