harnery 0.43.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +41 -48
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +38 -11
- package/dist/commands/diagnostics.d.ts.map +1 -1
- package/dist/commands/diagnostics.js +10 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -7
- package/dist/commands/governor.js +22 -10
- package/dist/commands/inbox.d.ts +1 -1
- package/dist/commands/inbox.d.ts.map +1 -1
- package/dist/commands/inbox.js +55 -2
- package/dist/commands/rm.d.ts.map +1 -1
- package/dist/commands/rm.js +28 -4
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +16 -1
- package/dist/commands/supervisor.d.ts.map +1 -1
- package/dist/commands/supervisor.js +7 -2
- package/dist/commands/toc.d.ts +14 -0
- package/dist/commands/toc.d.ts.map +1 -1
- package/dist/commands/toc.js +7 -4
- package/dist/commands/workflow.js +24 -30
- package/dist/core/agents/live-authority-v3.d.ts +1 -0
- package/dist/core/agents/live-authority-v3.d.ts.map +1 -1
- package/dist/core/agents/live-authority-v3.js +11 -0
- package/dist/core/agents/live-lifecycle-v3.d.ts +4 -2
- package/dist/core/agents/live-lifecycle-v3.d.ts.map +1 -1
- package/dist/core/agents/live-lifecycle-v3.js +28 -7
- package/dist/core/agents/mailbox.d.ts +9 -8
- package/dist/core/agents/mailbox.d.ts.map +1 -1
- package/dist/core/agents/mailbox.js +24 -17
- package/dist/core/agents/render/prompt-context.d.ts +2 -0
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +8 -12
- package/dist/core/agents/render/session-context.d.ts +2 -8
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +7 -30
- package/dist/core/agents/rules/stop-hook.js +3 -3
- package/dist/core/agents/state/heartbeat-writer.d.ts.map +1 -1
- package/dist/core/agents/state/heartbeat-writer.js +25 -14
- package/dist/core/agents/state/live-coordination-writer.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-writer.js +5 -0
- package/dist/core/agents/state/pidmap.d.ts.map +1 -1
- package/dist/core/agents/state/pidmap.js +5 -0
- package/dist/core/agents/state/proc-start.d.ts +19 -0
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +41 -0
- package/dist/core/agents/state/stale-sweep.d.ts.map +1 -1
- package/dist/core/agents/state/stale-sweep.js +180 -70
- package/dist/core/agents/state/unchanged-file.d.ts +44 -0
- package/dist/core/agents/state/unchanged-file.d.ts.map +1 -0
- package/dist/core/agents/state/unchanged-file.js +390 -0
- package/dist/core/context/index.d.ts +22 -10
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +381 -119
- package/dist/core/context/observation.d.ts +26 -0
- package/dist/core/context/observation.d.ts.map +1 -0
- package/dist/core/context/observation.js +11 -0
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +25 -14
- package/dist/core/diagnostics/path-identity.d.ts +3 -0
- package/dist/core/diagnostics/path-identity.d.ts.map +1 -0
- package/dist/core/diagnostics/path-identity.js +12 -0
- package/dist/core/diagnostics/sanitize.d.ts.map +1 -1
- package/dist/core/diagnostics/sanitize.js +4 -3
- package/dist/core/events/v3/authority-outbox.d.ts.map +1 -1
- package/dist/core/events/v3/authority-outbox.js +29 -0
- package/dist/core/events/v3/generation-custody.d.ts +5 -0
- package/dist/core/events/v3/generation-custody.d.ts.map +1 -0
- package/dist/core/events/v3/generation-custody.js +50 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts +1 -0
- package/dist/core/events/v3/producers/coordination-recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/coordination-recorder.js +7 -0
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +77 -64
- package/dist/core/governor/planning.d.ts.map +1 -1
- package/dist/core/governor/planning.js +14 -1
- package/dist/core/governor/service-read.d.ts +10 -2
- package/dist/core/governor/service-read.d.ts.map +1 -1
- package/dist/core/governor/service-read.js +105 -22
- package/dist/core/governor/service.d.ts +20 -2
- package/dist/core/governor/service.d.ts.map +1 -1
- package/dist/core/governor/service.js +72 -47
- package/dist/core/hooks/cli.js +33 -5
- package/dist/core/inbox/contract.d.ts +2 -1
- package/dist/core/inbox/contract.d.ts.map +1 -1
- package/dist/core/inbox/index.d.ts +2 -0
- package/dist/core/inbox/index.d.ts.map +1 -1
- package/dist/core/inbox/index.js +2 -0
- package/dist/core/inbox/peer-messages.d.ts +37 -0
- package/dist/core/inbox/peer-messages.d.ts.map +1 -0
- package/dist/core/inbox/peer-messages.js +72 -0
- package/dist/core/inbox/service.d.ts +8 -1
- package/dist/core/inbox/service.d.ts.map +1 -1
- package/dist/core/inbox/service.js +67 -21
- package/dist/core/inbox/targets.d.ts +22 -0
- package/dist/core/inbox/targets.d.ts.map +1 -0
- package/dist/core/inbox/targets.js +108 -0
- package/dist/core/inbox/watcher.js +7 -3
- package/dist/core/journal/index.d.ts.map +1 -1
- package/dist/core/journal/index.js +17 -2
- package/dist/core/resources/contract.d.ts +3 -0
- package/dist/core/resources/contract.d.ts.map +1 -1
- package/dist/core/resources/service-status.d.ts +14 -1
- package/dist/core/resources/service-status.d.ts.map +1 -1
- package/dist/core/resources/service-status.js +23 -13
- package/dist/core/resources/service.d.ts +9 -1
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +47 -27
- package/dist/core/resources/status.d.ts +2 -5
- package/dist/core/resources/status.d.ts.map +1 -1
- package/dist/core/resources/status.js +36 -7
- package/dist/core/servers/index.d.ts +24 -13
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +132 -92
- package/dist/core/servers/tunnels.d.ts +3 -5
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +3 -9
- package/dist/core/supervisor/contract.d.ts +12 -0
- package/dist/core/supervisor/contract.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.d.ts.map +1 -1
- package/dist/core/supervisor/explanations.js +2 -1
- package/dist/core/supervisor/findings.d.ts.map +1 -1
- package/dist/core/supervisor/findings.js +54 -9
- package/dist/core/supervisor/service.d.ts +9 -1
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/supervisor/service.js +59 -29
- package/dist/core/supervisor/services.d.ts.map +1 -1
- package/dist/core/supervisor/services.js +22 -1
- package/dist/core/supervisor/status.d.ts +17 -2
- package/dist/core/supervisor/status.d.ts.map +1 -1
- package/dist/core/supervisor/status.js +70 -12
- package/dist/core/supervisor/timeline.d.ts.map +1 -1
- package/dist/core/supervisor/timeline.js +2 -1
- package/dist/core/work/state.js +7 -7
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +105 -32
- package/dist/core/workflow/failure-evidence.d.ts +16 -0
- package/dist/core/workflow/failure-evidence.d.ts.map +1 -0
- package/dist/core/workflow/failure-evidence.js +114 -0
- package/dist/core/workflow/index.d.ts +1 -1
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +1 -1
- package/dist/core/workflow/live-session-v3.d.ts +6 -0
- package/dist/core/workflow/live-session-v3.d.ts.map +1 -1
- package/dist/core/workflow/live-session-v3.js +126 -5
- package/dist/core/workflow/proof.d.ts +5 -5
- package/dist/core/workflow/proof.d.ts.map +1 -1
- package/dist/core/workflow/proof.js +115 -20
- package/dist/core/workflow/run-state.d.ts +3 -1
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +31 -27
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +19 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +13 -0
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +19 -0
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +14 -0
- package/dist/core/workflow/types.d.ts +64 -17
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.d.ts +0 -1
- package/dist/core/workflow/workspaces/index.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/index.js +0 -1
- package/dist/core/workflow/workspaces/leases.d.ts +3 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +30 -16
- package/dist/core/workflow/workspaces/local-git.d.ts +6 -2
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +270 -6
- package/dist/core/workflow/workspaces/ownership.d.ts +11 -0
- package/dist/core/workflow/workspaces/ownership.d.ts.map +1 -0
- package/dist/core/workflow/workspaces/ownership.js +194 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts +35 -0
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/reclaim.js +14 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +21 -13
- package/dist/core/workflow/workspaces/state.d.ts +1 -0
- package/dist/core/workflow/workspaces/state.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/state.js +21 -1
- package/dist/lib/browser/windows-chrome.d.ts +100 -12
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -1
- package/dist/lib/browser/windows-chrome.js +262 -73
- package/dist/lib/docs-links.d.ts.map +1 -1
- package/dist/lib/docs-links.js +45 -2
- package/dist/lib/guarded-remove.d.ts +7 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +106 -12
- package/dist/lib/identities/assume.d.ts.map +1 -1
- package/dist/lib/identities/assume.js +4 -0
- package/dist/lib/zip/index.d.ts +41 -0
- package/dist/lib/zip/index.d.ts.map +1 -0
- package/dist/lib/zip/index.js +398 -0
- package/package.json +7 -1
- package/src/commands/agents.ts +54 -63
- package/src/commands/browse.ts +50 -14
- package/src/commands/diagnostics.ts +10 -0
- package/src/commands/doctor.ts +26 -7
- package/src/commands/governor.ts +23 -10
- package/src/commands/inbox.ts +52 -2
- package/src/commands/rm.ts +43 -4
- package/src/commands/servers.ts +16 -4
- package/src/commands/supervisor.ts +7 -2
- package/src/commands/toc.ts +14 -6
- package/src/commands/workflow.ts +30 -30
- package/src/core/agents/live-authority-v3.ts +17 -0
- package/src/core/agents/live-lifecycle-v3.ts +52 -14
- package/src/core/agents/mailbox.ts +26 -17
- package/src/core/agents/render/prompt-context.ts +10 -12
- package/src/core/agents/render/session-context.ts +9 -32
- package/src/core/agents/rules/stop-hook.ts +3 -3
- package/src/core/agents/state/heartbeat-writer.ts +29 -12
- package/src/core/agents/state/live-coordination-writer.ts +29 -0
- package/src/core/agents/state/pidmap.ts +13 -0
- package/src/core/agents/state/proc-start.ts +57 -0
- package/src/core/agents/state/stale-sweep.ts +247 -82
- package/src/core/agents/state/unchanged-file.ts +456 -0
- package/src/core/context/index.ts +446 -136
- package/src/core/context/observation.ts +29 -0
- package/src/core/diagnostics/bundle.ts +23 -18
- package/src/core/diagnostics/path-identity.ts +15 -0
- package/src/core/diagnostics/sanitize.ts +4 -3
- package/src/core/events/v3/authority-outbox.ts +56 -0
- package/src/core/events/v3/generation-custody.ts +59 -0
- package/src/core/events/v3/producers/coordination-recorder.ts +15 -0
- package/src/core/events/v3/producers/recorder.ts +94 -68
- package/src/core/governor/planning.ts +20 -8
- package/src/core/governor/service-read.ts +128 -20
- package/src/core/governor/service.ts +119 -42
- package/src/core/hooks/cli.ts +33 -5
- package/src/core/inbox/contract.ts +2 -1
- package/src/core/inbox/index.ts +2 -0
- package/src/core/inbox/peer-messages.ts +94 -0
- package/src/core/inbox/service.ts +88 -22
- package/src/core/inbox/targets.ts +148 -0
- package/src/core/inbox/watcher.ts +8 -9
- package/src/core/journal/index.ts +19 -2
- package/src/core/resources/contract.ts +4 -0
- package/src/core/resources/service-status.ts +35 -12
- package/src/core/resources/service.ts +71 -24
- package/src/core/resources/status.ts +57 -8
- package/src/core/servers/index.ts +177 -91
- package/src/core/servers/tunnels.ts +3 -11
- package/src/core/supervisor/contract.ts +13 -0
- package/src/core/supervisor/explanations.ts +4 -1
- package/src/core/supervisor/findings.ts +68 -10
- package/src/core/supervisor/service.ts +88 -26
- package/src/core/supervisor/services.ts +26 -8
- package/src/core/supervisor/status.ts +89 -12
- package/src/core/supervisor/timeline.ts +2 -1
- package/src/core/work/state.ts +7 -7
- package/src/core/workflow/engine.ts +131 -49
- package/src/core/workflow/failure-evidence.ts +141 -0
- package/src/core/workflow/index.ts +0 -1
- package/src/core/workflow/live-session-v3.ts +146 -5
- package/src/core/workflow/proof.ts +134 -26
- package/src/core/workflow/run-state.ts +31 -29
- package/src/core/workflow/spawn-claude.ts +18 -0
- package/src/core/workflow/spawn-codex.ts +13 -0
- package/src/core/workflow/spawn-cursor.ts +18 -0
- package/src/core/workflow/spawn-opencode.ts +14 -0
- package/src/core/workflow/types.ts +51 -17
- package/src/core/workflow/workspaces/index.ts +0 -1
- package/src/core/workflow/workspaces/leases.ts +48 -17
- package/src/core/workflow/workspaces/local-git.ts +315 -5
- package/src/core/workflow/workspaces/ownership.ts +232 -0
- package/src/core/workflow/workspaces/reclaim.ts +58 -0
- package/src/core/workflow/workspaces/recovery.ts +25 -13
- package/src/core/workflow/workspaces/state.ts +20 -0
- package/src/lib/browser/windows-chrome.ts +346 -86
- package/src/lib/docs-links.ts +49 -2
- package/src/lib/guarded-remove.ts +132 -14
- package/src/lib/identities/assume.ts +4 -0
- package/src/lib/zip/index.ts +483 -0
|
@@ -7,13 +7,21 @@
|
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
10
|
-
import { dirname, join } from "node:path";
|
|
10
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
11
11
|
import { readLiveCoordinationRow } from "../agents/state/live-coordination-view.js";
|
|
12
|
+
import { resolveBinName } from "../config.js";
|
|
13
|
+
import { peerInbox } from "../inbox/peer-messages.js";
|
|
14
|
+
import { project } from "../inbox/service.js";
|
|
12
15
|
import { stateFileMode } from "../storage/modes.js";
|
|
16
|
+
import { acquireNoClobberLease } from "../workflow/workspaces/leases.js";
|
|
17
|
+
import { observationsComparable } from "./observation.js";
|
|
18
|
+
export { observationsComparable } from "./observation.js";
|
|
13
19
|
export const CONTEXT_SCHEMA_VERSION = 1;
|
|
14
20
|
export const MAX_CAPSULE_BYTES = 32 * 1024;
|
|
15
21
|
const MAX_SNAPSHOT_PATHS = 15;
|
|
16
22
|
const MAX_SNAPSHOT_PATH_CHARS = 500;
|
|
23
|
+
const MAX_OBSERVATION_DETAIL_CHARS = 500;
|
|
24
|
+
const MAX_RECOVERY_INPUT_BODY_BYTES = 160;
|
|
17
25
|
/** Parse the context-window shapes exposed by current adapter hook/status data. */
|
|
18
26
|
export function extractContextSample(payload, options) {
|
|
19
27
|
const contextValue = payload.context_window;
|
|
@@ -62,19 +70,26 @@ export function extractContextSample(payload, options) {
|
|
|
62
70
|
}
|
|
63
71
|
/** Persist a latest context sample. Identical measurements are a no-op. */
|
|
64
72
|
export function recordContextSample(coordRoot, instanceId, sample) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
latest_context
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
return withContextMutation(coordRoot, sample.session_id, () => {
|
|
74
|
+
const current = readContextState(coordRoot, sample.session_id);
|
|
75
|
+
const state = current?.instance_id === instanceId
|
|
76
|
+
? current
|
|
77
|
+
: {
|
|
78
|
+
...newState({ sessionId: sample.session_id, instanceId, now: sample.observed_at }),
|
|
79
|
+
generation: current?.generation ?? 0,
|
|
80
|
+
};
|
|
81
|
+
const changed = !sameMeasurement(state.latest_context, sample);
|
|
82
|
+
if (!changed)
|
|
83
|
+
return { state, changed: false };
|
|
84
|
+
const next = {
|
|
85
|
+
...state,
|
|
86
|
+
instance_id: instanceId,
|
|
87
|
+
latest_context: sample,
|
|
88
|
+
updated_at: sample.observed_at,
|
|
89
|
+
};
|
|
90
|
+
writeState(coordRoot, next);
|
|
91
|
+
return { state: next, changed: true };
|
|
92
|
+
});
|
|
78
93
|
}
|
|
79
94
|
/**
|
|
80
95
|
* Snapshot external work state. Repeated PreCompact events reuse the existing
|
|
@@ -82,109 +97,213 @@ export function recordContextSample(coordRoot, instanceId, sample) {
|
|
|
82
97
|
* create a new generation.
|
|
83
98
|
*/
|
|
84
99
|
export function checkpointContext(coordRoot, input) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
path: join(coordRoot, ".harnery", current.latest_capsule),
|
|
94
|
-
state: current,
|
|
95
|
-
reused: true,
|
|
100
|
+
return withContextMutation(coordRoot, input.sessionId, () => {
|
|
101
|
+
const now = new Date().toISOString();
|
|
102
|
+
const previous = readContextState(coordRoot, input.sessionId);
|
|
103
|
+
const current = previous?.instance_id === input.instanceId
|
|
104
|
+
? previous
|
|
105
|
+
: {
|
|
106
|
+
...newState({ sessionId: input.sessionId, instanceId: input.instanceId, now }),
|
|
107
|
+
generation: previous?.generation ?? 0,
|
|
96
108
|
};
|
|
109
|
+
if (input.reason === "pre_compact" && current.phase === "checkpointed") {
|
|
110
|
+
const existing = readLatestCapsule(coordRoot, input.sessionId);
|
|
111
|
+
if (existing && capsuleMatchesState(current, existing, input) && current.latest_capsule) {
|
|
112
|
+
return {
|
|
113
|
+
capsule: existing,
|
|
114
|
+
path: join(coordRoot, ".harnery", current.latest_capsule),
|
|
115
|
+
state: current,
|
|
116
|
+
reused: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
97
119
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
const heartbeat = readHeartbeat(coordRoot, input.instanceId);
|
|
121
|
+
const generation = current.generation + 1;
|
|
122
|
+
const capsule = {
|
|
123
|
+
schema_version: CONTEXT_SCHEMA_VERSION,
|
|
124
|
+
capsule_id: randomUUID(),
|
|
125
|
+
generation,
|
|
126
|
+
created_at: now,
|
|
127
|
+
reason: input.reason,
|
|
128
|
+
session: {
|
|
129
|
+
session_id: input.sessionId,
|
|
130
|
+
instance_id: input.instanceId,
|
|
131
|
+
adapter: input.adapter,
|
|
132
|
+
model: input.model ?? current.latest_context?.model ?? heartbeat?.model,
|
|
133
|
+
},
|
|
134
|
+
context: current.latest_context,
|
|
135
|
+
work: buildWorkSnapshot(heartbeat, input.continuationNote),
|
|
136
|
+
repo: snapshotRepo(input.cwd),
|
|
137
|
+
};
|
|
138
|
+
const relativePath = capsuleRelativePath(input.sessionId, generation);
|
|
139
|
+
const absolutePath = join(coordRoot, ".harnery", relativePath);
|
|
140
|
+
writeBoundedJson(absolutePath, capsule, MAX_CAPSULE_BYTES);
|
|
141
|
+
const next = {
|
|
142
|
+
...current,
|
|
109
143
|
instance_id: input.instanceId,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
...current,
|
|
122
|
-
instance_id: input.instanceId,
|
|
123
|
-
phase: "checkpointed",
|
|
124
|
-
generation,
|
|
125
|
-
latest_capsule: relativePath,
|
|
126
|
-
updated_at: now,
|
|
127
|
-
recovered_at: undefined,
|
|
128
|
-
compaction_completed_at: undefined,
|
|
129
|
-
degraded_reason: undefined,
|
|
130
|
-
};
|
|
131
|
-
writeState(coordRoot, next);
|
|
132
|
-
return { capsule, path: absolutePath, state: next, reused: false };
|
|
144
|
+
phase: "checkpointed",
|
|
145
|
+
generation,
|
|
146
|
+
latest_capsule: relativePath,
|
|
147
|
+
updated_at: now,
|
|
148
|
+
recovered_at: undefined,
|
|
149
|
+
compaction_completed_at: undefined,
|
|
150
|
+
degraded_reason: undefined,
|
|
151
|
+
};
|
|
152
|
+
writeState(coordRoot, next);
|
|
153
|
+
return { capsule, path: absolutePath, state: next, reused: false };
|
|
154
|
+
});
|
|
133
155
|
}
|
|
134
156
|
/** Prepare a briefing without claiming that a adapter has accepted it yet. */
|
|
135
157
|
export function prepareContextRecovery(coordRoot, input) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
158
|
+
return withContextMutation(coordRoot, input.sessionId, () => {
|
|
159
|
+
const now = new Date().toISOString();
|
|
160
|
+
const current = readContextState(coordRoot, input.sessionId) ??
|
|
161
|
+
newState({ sessionId: input.sessionId, instanceId: input.instanceId, now });
|
|
162
|
+
// A reused provider ID does not transfer the prior task's context.
|
|
163
|
+
if (current.instance_id !== input.instanceId)
|
|
164
|
+
return null;
|
|
165
|
+
const capsule = readLatestCapsule(coordRoot, input.sessionId);
|
|
166
|
+
if (!capsule || !capsuleMatchesState(current, capsule, input)) {
|
|
167
|
+
const degraded = {
|
|
168
|
+
...current,
|
|
169
|
+
instance_id: input.instanceId,
|
|
170
|
+
phase: "degraded",
|
|
171
|
+
updated_at: now,
|
|
172
|
+
degraded_reason: capsule
|
|
173
|
+
? "continuity checkpoint does not match the requesting instance and state"
|
|
174
|
+
: "post-compaction signal arrived without a continuity checkpoint",
|
|
175
|
+
};
|
|
176
|
+
writeState(coordRoot, degraded);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const liveRepo = snapshotRepo(input.cwd);
|
|
180
|
+
const briefing = [
|
|
181
|
+
renderRecoveryBriefing(capsule, liveRepo),
|
|
182
|
+
renderRecoveryInput(coordRoot, input.instanceId),
|
|
183
|
+
]
|
|
184
|
+
.filter(Boolean)
|
|
185
|
+
.join("\n\n");
|
|
186
|
+
return {
|
|
187
|
+
state: current,
|
|
188
|
+
capsule,
|
|
189
|
+
briefing,
|
|
190
|
+
checkpoint: Object.freeze({
|
|
191
|
+
session_id: input.sessionId,
|
|
192
|
+
instance_id: input.instanceId,
|
|
193
|
+
generation: capsule.generation,
|
|
194
|
+
capsule_id: capsule.capsule_id,
|
|
195
|
+
capsule_sha256: capsuleDigest(capsule),
|
|
196
|
+
}),
|
|
147
197
|
};
|
|
148
|
-
|
|
149
|
-
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/** Recovery lists input without surfacing it again or changing its receipts. */
|
|
201
|
+
function renderRecoveryInput(coordRoot, instanceId) {
|
|
202
|
+
const readCommand = `${resolveBinName(coordRoot)} agents inbox list ${instanceId}`;
|
|
203
|
+
try {
|
|
204
|
+
const records = peerInbox(coordRoot).records(instanceId);
|
|
205
|
+
if (records.some((record) => (record.kind !== "message" && record.kind !== "surfaced") ||
|
|
206
|
+
typeof record.message_id !== "string" ||
|
|
207
|
+
Buffer.byteLength(record.message_id) > 128)) {
|
|
208
|
+
throw new Error("inbox input cannot be listed safely");
|
|
209
|
+
}
|
|
210
|
+
const { surfaced } = project(records);
|
|
211
|
+
const messages = records.filter((record) => record.kind === "message" && record.recipient_instance_id === instanceId);
|
|
212
|
+
const pending = messages.filter((message) => !surfaced.has(message.message_id));
|
|
213
|
+
const recent = messages.filter((message) => surfaced.has(message.message_id)).slice(-10);
|
|
214
|
+
if (pending.length === 0 && recent.length === 0)
|
|
215
|
+
return "";
|
|
216
|
+
const lines = [
|
|
217
|
+
"Coordination input after compaction (surfaced does not mean read):",
|
|
218
|
+
...pending
|
|
219
|
+
.slice(0, 25)
|
|
220
|
+
.map((message) => inputLine(message.message_id, "pending", message.body)),
|
|
221
|
+
...recent.map((message) => inputLine(message.message_id, "previously surfaced", message.body)),
|
|
222
|
+
];
|
|
223
|
+
if (pending.length > 25)
|
|
224
|
+
lines.push(`${pending.length - 25} more pending messages are not listed.`);
|
|
225
|
+
lines.push(`Recovery did not acknowledge these messages. Read pending input with \`${readCommand}\`.`);
|
|
226
|
+
return lines.join("\n");
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return `Coordination input could not be read. Check \`${readCommand}\` before continuing.`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
function inputLine(messageId, status, body) {
|
|
233
|
+
const text = body.replace(/\s+/g, " ").trim();
|
|
234
|
+
let excerpt = "";
|
|
235
|
+
let bytes = 0;
|
|
236
|
+
for (const character of text) {
|
|
237
|
+
const size = Buffer.byteLength(character);
|
|
238
|
+
if (bytes + size > MAX_RECOVERY_INPUT_BODY_BYTES)
|
|
239
|
+
break;
|
|
240
|
+
excerpt += character;
|
|
241
|
+
bytes += size;
|
|
150
242
|
}
|
|
151
|
-
|
|
152
|
-
const briefing = renderRecoveryBriefing(capsule, liveRepo);
|
|
153
|
-
return { state: current, capsule, briefing };
|
|
243
|
+
return `- ${messageId} (${status}): ${excerpt}${excerpt.length < text.length ? " [excerpt]" : ""}`;
|
|
154
244
|
}
|
|
155
|
-
/**
|
|
245
|
+
/**
|
|
246
|
+
* Advance continuity only after the adapter accepted the prepared briefing.
|
|
247
|
+
* Missing, altered, foreign or superseded checkpoints refuse without writing.
|
|
248
|
+
* Repeating the same accepted checkpoint is an exact no-op.
|
|
249
|
+
*/
|
|
156
250
|
export function completeContextRecovery(coordRoot, input) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
251
|
+
return withContextMutation(coordRoot, input.sessionId, () => {
|
|
252
|
+
const current = readContextState(coordRoot, input.sessionId);
|
|
253
|
+
const checkpoint = input.checkpoint;
|
|
254
|
+
if (!current ||
|
|
255
|
+
!checkpoint ||
|
|
256
|
+
checkpoint.session_id !== input.sessionId ||
|
|
257
|
+
checkpoint.instance_id !== input.instanceId ||
|
|
258
|
+
!Number.isSafeInteger(checkpoint.generation) ||
|
|
259
|
+
checkpoint.generation < 1 ||
|
|
260
|
+
typeof checkpoint.capsule_id !== "string" ||
|
|
261
|
+
!checkpoint.capsule_id ||
|
|
262
|
+
!/^[a-f0-9]{64}$/.test(checkpoint.capsule_sha256) ||
|
|
263
|
+
(current.phase !== "checkpointed" && current.phase !== "recovered"))
|
|
264
|
+
return null;
|
|
265
|
+
const capsule = readLatestCapsule(coordRoot, input.sessionId);
|
|
266
|
+
if (!capsule ||
|
|
267
|
+
!capsuleMatchesState(current, capsule, input) ||
|
|
268
|
+
checkpoint.generation !== capsule.generation ||
|
|
269
|
+
checkpoint.capsule_id !== capsule.capsule_id ||
|
|
270
|
+
checkpoint.capsule_sha256 !== capsuleDigest(capsule))
|
|
271
|
+
return null;
|
|
272
|
+
if (current.phase === "recovered")
|
|
273
|
+
return current;
|
|
274
|
+
const now = new Date().toISOString();
|
|
275
|
+
const recovered = {
|
|
276
|
+
...current,
|
|
277
|
+
phase: "recovered",
|
|
278
|
+
updated_at: now,
|
|
279
|
+
recovered_at: now,
|
|
280
|
+
degraded_reason: undefined,
|
|
281
|
+
};
|
|
282
|
+
writeState(coordRoot, recovered);
|
|
283
|
+
return recovered;
|
|
284
|
+
});
|
|
170
285
|
}
|
|
171
286
|
/** Record that the adapter, rather than Harnery, reported compaction complete. */
|
|
172
287
|
export function markContextCompactionCompleted(coordRoot, input) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
288
|
+
return withContextMutation(coordRoot, input.sessionId, () => {
|
|
289
|
+
const now = input.observedAt ?? new Date().toISOString();
|
|
290
|
+
const current = readContextState(coordRoot, input.sessionId) ??
|
|
291
|
+
newState({ sessionId: input.sessionId, instanceId: input.instanceId, now });
|
|
292
|
+
if (current.instance_id !== input.instanceId)
|
|
293
|
+
return current;
|
|
294
|
+
const next = {
|
|
295
|
+
...current,
|
|
296
|
+
instance_id: input.instanceId,
|
|
297
|
+
phase: current.latest_capsule ? current.phase : "degraded",
|
|
298
|
+
compaction_completed_at: now,
|
|
299
|
+
updated_at: now,
|
|
300
|
+
degraded_reason: current.latest_capsule
|
|
301
|
+
? current.degraded_reason
|
|
302
|
+
: "post-compaction signal arrived without a continuity checkpoint",
|
|
303
|
+
};
|
|
304
|
+
writeState(coordRoot, next);
|
|
305
|
+
return next;
|
|
306
|
+
});
|
|
188
307
|
}
|
|
189
308
|
/** Convenience API for callers whose injection is synchronous and guaranteed. */
|
|
190
309
|
export function recoverContext(coordRoot, input) {
|
|
@@ -193,7 +312,19 @@ export function recoverContext(coordRoot, input) {
|
|
|
193
312
|
const state = readContextState(coordRoot, input.sessionId);
|
|
194
313
|
return { state, capsule: null, briefing: "", recovered: false };
|
|
195
314
|
}
|
|
196
|
-
const recovered = completeContextRecovery(coordRoot,
|
|
315
|
+
const recovered = completeContextRecovery(coordRoot, {
|
|
316
|
+
sessionId: input.sessionId,
|
|
317
|
+
instanceId: input.instanceId,
|
|
318
|
+
checkpoint: prepared.checkpoint,
|
|
319
|
+
});
|
|
320
|
+
if (!recovered) {
|
|
321
|
+
return {
|
|
322
|
+
state: readContextState(coordRoot, input.sessionId) ?? prepared.state,
|
|
323
|
+
capsule: null,
|
|
324
|
+
briefing: "",
|
|
325
|
+
recovered: false,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
197
328
|
return {
|
|
198
329
|
state: recovered,
|
|
199
330
|
capsule: prepared.capsule,
|
|
@@ -206,9 +337,25 @@ export function readContextState(coordRoot, sessionId) {
|
|
|
206
337
|
}
|
|
207
338
|
export function readLatestCapsule(coordRoot, sessionId) {
|
|
208
339
|
const state = readContextState(coordRoot, sessionId);
|
|
209
|
-
if (
|
|
340
|
+
if (typeof state?.latest_capsule !== "string" || !state.latest_capsule)
|
|
341
|
+
return null;
|
|
342
|
+
const capsule = readJson(join(coordRoot, ".harnery", state.latest_capsule));
|
|
343
|
+
if (!capsule ||
|
|
344
|
+
!record(capsule.repo) ||
|
|
345
|
+
!record(capsule.work) ||
|
|
346
|
+
typeof capsule.repo.cwd !== "string" ||
|
|
347
|
+
!Array.isArray(capsule.repo.dirty_paths) ||
|
|
348
|
+
!capsule.repo.dirty_paths.every((path) => typeof path === "string") ||
|
|
349
|
+
!Array.isArray(capsule.work.files_held) ||
|
|
350
|
+
!capsule.work.files_held.every((path) => typeof path === "string")) {
|
|
210
351
|
return null;
|
|
211
|
-
|
|
352
|
+
}
|
|
353
|
+
capsule.repo.observation = repoObservation(capsule.repo.observation);
|
|
354
|
+
capsule.repo.observation_detail = clampString(string(capsule.repo.observation_detail), MAX_OBSERVATION_DETAIL_CHARS);
|
|
355
|
+
capsule.repo.branch = string(capsule.repo.branch)?.trim() || undefined;
|
|
356
|
+
capsule.repo.head = string(capsule.repo.head)?.trim() || undefined;
|
|
357
|
+
capsule.repo.root = string(capsule.repo.root)?.trim() || undefined;
|
|
358
|
+
return capsule;
|
|
212
359
|
}
|
|
213
360
|
export function renderRecoveryBriefing(capsule, liveRepo) {
|
|
214
361
|
const lines = [
|
|
@@ -222,6 +369,38 @@ export function renderRecoveryBriefing(capsule, liveRepo) {
|
|
|
222
369
|
if (capsule.work.files_held.length > 0) {
|
|
223
370
|
lines.push(`Files held: ${capsule.work.files_held.join(", ")}`);
|
|
224
371
|
}
|
|
372
|
+
const unknown = [];
|
|
373
|
+
if (!observationsComparable(capsule.repo, liveRepo)) {
|
|
374
|
+
for (const [label, repo] of [
|
|
375
|
+
["checkpoint", capsule.repo],
|
|
376
|
+
["current repository", liveRepo],
|
|
377
|
+
]) {
|
|
378
|
+
if (repo.observation !== "ok") {
|
|
379
|
+
const detail = clampString(string(repo.observation_detail), MAX_OBSERVATION_DETAIL_CHARS);
|
|
380
|
+
unknown.push(`${label}: ${repoObservation(repo.observation)}${detail ? ` (${detail})` : ""}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (capsule.repo.dirty_paths_truncated || liveRepo.dirty_paths_truncated) {
|
|
385
|
+
unknown.push("dirty path list is truncated");
|
|
386
|
+
}
|
|
387
|
+
if (!capsule.repo.root ||
|
|
388
|
+
!liveRepo.root ||
|
|
389
|
+
!isAbsolute(capsule.repo.root) ||
|
|
390
|
+
!isAbsolute(liveRepo.root)) {
|
|
391
|
+
unknown.push("repository root is unknown");
|
|
392
|
+
}
|
|
393
|
+
else if (capsule.repo.root !== liveRepo.root) {
|
|
394
|
+
unknown.push("repository root differs from the checkpoint");
|
|
395
|
+
}
|
|
396
|
+
if (!capsule.repo.branch?.trim() || !liveRepo.branch?.trim())
|
|
397
|
+
unknown.push("branch is unknown");
|
|
398
|
+
if (!capsule.repo.head?.trim() || !liveRepo.head?.trim())
|
|
399
|
+
unknown.push("HEAD is unknown");
|
|
400
|
+
if (unknown.length > 0) {
|
|
401
|
+
lines.push(`Repository could not be verified against the checkpoint: ${unknown.join("; ")}. Re-check before editing.`);
|
|
402
|
+
return lines.join("\n");
|
|
403
|
+
}
|
|
225
404
|
const drift = [];
|
|
226
405
|
if (capsule.repo.branch && liveRepo.branch && capsule.repo.branch !== liveRepo.branch) {
|
|
227
406
|
drift.push(`branch changed ${capsule.repo.branch} -> ${liveRepo.branch}`);
|
|
@@ -244,24 +423,58 @@ export function renderRecoveryBriefing(capsule, liveRepo) {
|
|
|
244
423
|
}
|
|
245
424
|
export function snapshotRepo(cwd) {
|
|
246
425
|
const root = git(cwd, ["rev-parse", "--show-toplevel"]);
|
|
247
|
-
if (!root)
|
|
248
|
-
return {
|
|
426
|
+
if (!root.ok || !root.out.trim()) {
|
|
427
|
+
return {
|
|
428
|
+
cwd: clampString(cwd, 2_000) ?? cwd,
|
|
429
|
+
observation: "unavailable",
|
|
430
|
+
observation_detail: `top-level: ${root.ok ? "empty result" : root.reason}`,
|
|
431
|
+
dirty_paths: [],
|
|
432
|
+
};
|
|
433
|
+
}
|
|
249
434
|
const status = git(cwd, ["status", "--porcelain=v1", "--untracked-files=normal"]);
|
|
250
|
-
const
|
|
251
|
-
|
|
435
|
+
const branch = git(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
436
|
+
const head = git(cwd, ["rev-parse", "HEAD"]);
|
|
437
|
+
const reads = [
|
|
438
|
+
["status", status],
|
|
439
|
+
["branch", branch],
|
|
440
|
+
["HEAD", head],
|
|
441
|
+
];
|
|
442
|
+
const details = reads.flatMap(([label, read]) => !read.ok
|
|
443
|
+
? [`${label}: ${read.reason}`]
|
|
444
|
+
: label !== "status" && !read.out.trim()
|
|
445
|
+
? [`${label}: empty result`]
|
|
446
|
+
: []);
|
|
447
|
+
const dirty = status.ok
|
|
448
|
+
? status.out
|
|
252
449
|
.split("\n")
|
|
253
450
|
.map((line) => line.trimEnd())
|
|
254
451
|
.filter(Boolean)
|
|
255
|
-
.map((line) =>
|
|
452
|
+
.map((line) => line.slice(3).trim())
|
|
256
453
|
.filter(Boolean)
|
|
257
454
|
: [];
|
|
455
|
+
const truncated = dirty.length > MAX_SNAPSHOT_PATHS ||
|
|
456
|
+
dirty.some((path) => path.length > MAX_SNAPSHOT_PATH_CHARS);
|
|
457
|
+
if (truncated)
|
|
458
|
+
details.push("dirty path list is truncated");
|
|
258
459
|
return {
|
|
259
460
|
cwd: clampString(cwd, 2_000) ?? cwd,
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
461
|
+
observation: reads.some(([, read]) => !read.ok && read.reason === "timed_out")
|
|
462
|
+
? "unavailable"
|
|
463
|
+
: details.length > 0
|
|
464
|
+
? "partial"
|
|
465
|
+
: "ok",
|
|
466
|
+
...(details.length > 0
|
|
467
|
+
? {
|
|
468
|
+
observation_detail: clampString(details.join("; "), MAX_OBSERVATION_DETAIL_CHARS),
|
|
469
|
+
}
|
|
470
|
+
: {}),
|
|
471
|
+
root: clampString(root.out.trim(), 2_000),
|
|
472
|
+
branch: branch.ok ? branch.out.trim() || undefined : undefined,
|
|
473
|
+
head: head.ok ? head.out.trim() || undefined : undefined,
|
|
474
|
+
dirty_paths: dirty
|
|
475
|
+
.slice(0, MAX_SNAPSHOT_PATHS)
|
|
476
|
+
.map((path) => path.slice(0, MAX_SNAPSHOT_PATH_CHARS)),
|
|
477
|
+
...(truncated ? { dirty_paths_truncated: true } : {}),
|
|
265
478
|
};
|
|
266
479
|
}
|
|
267
480
|
function buildWorkSnapshot(heartbeat, continuationNote) {
|
|
@@ -297,6 +510,39 @@ function newState(input) {
|
|
|
297
510
|
updated_at: input.now,
|
|
298
511
|
};
|
|
299
512
|
}
|
|
513
|
+
function capsuleMatchesState(state, capsule, input) {
|
|
514
|
+
return (state.session_id === input.sessionId &&
|
|
515
|
+
state.instance_id === input.instanceId &&
|
|
516
|
+
capsule.session?.session_id === input.sessionId &&
|
|
517
|
+
capsule.session?.instance_id === input.instanceId &&
|
|
518
|
+
capsule.schema_version === CONTEXT_SCHEMA_VERSION &&
|
|
519
|
+
Number.isSafeInteger(capsule.generation) &&
|
|
520
|
+
capsule.generation > 0 &&
|
|
521
|
+
capsule.generation === state.generation &&
|
|
522
|
+
typeof capsule.capsule_id === "string" &&
|
|
523
|
+
capsule.capsule_id.length > 0 &&
|
|
524
|
+
state.latest_capsule === capsuleRelativePath(input.sessionId, capsule.generation));
|
|
525
|
+
}
|
|
526
|
+
function capsuleDigest(capsule) {
|
|
527
|
+
return createHash("sha256").update(JSON.stringify(capsule)).digest("hex");
|
|
528
|
+
}
|
|
529
|
+
/** Every state writer shares the same crash-recoverable session reservation. */
|
|
530
|
+
function withContextMutation(coordRoot, sessionId, action) {
|
|
531
|
+
const authority = createHash("sha256").update(sessionId).digest("hex");
|
|
532
|
+
const lease = acquireNoClobberLease({
|
|
533
|
+
path: join(contextDir(coordRoot, sessionId), "mutation.lease"),
|
|
534
|
+
scope: "context-continuity",
|
|
535
|
+
authoritySha256: authority,
|
|
536
|
+
staleAfterMs: 30_000,
|
|
537
|
+
validateStaleOwner: (owner) => owner.authority_sha256 === authority,
|
|
538
|
+
});
|
|
539
|
+
try {
|
|
540
|
+
return action();
|
|
541
|
+
}
|
|
542
|
+
finally {
|
|
543
|
+
lease.release();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
300
546
|
function writeState(coordRoot, state) {
|
|
301
547
|
writeBoundedJson(statePath(coordRoot, state.session_id), state, MAX_CAPSULE_BYTES);
|
|
302
548
|
}
|
|
@@ -336,12 +582,28 @@ function readJson(path) {
|
|
|
336
582
|
function git(cwd, args) {
|
|
337
583
|
try {
|
|
338
584
|
const result = spawnSync("git", args, { cwd, encoding: "utf8", timeout: 2_000 });
|
|
339
|
-
|
|
585
|
+
if (result.error) {
|
|
586
|
+
return {
|
|
587
|
+
ok: false,
|
|
588
|
+
reason: result.error.code === "ETIMEDOUT"
|
|
589
|
+
? "timed_out"
|
|
590
|
+
: "spawn_error",
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
if (result.status === 0)
|
|
594
|
+
return { ok: true, out: result.stdout.trimEnd() };
|
|
595
|
+
return {
|
|
596
|
+
ok: false,
|
|
597
|
+
reason: /not a git repository/i.test(result.stderr) ? "not_repository" : "failed",
|
|
598
|
+
};
|
|
340
599
|
}
|
|
341
600
|
catch {
|
|
342
|
-
return
|
|
601
|
+
return { ok: false, reason: "spawn_error" };
|
|
343
602
|
}
|
|
344
603
|
}
|
|
604
|
+
function repoObservation(value) {
|
|
605
|
+
return value === "ok" || value === "partial" ? value : "unavailable";
|
|
606
|
+
}
|
|
345
607
|
function sameMeasurement(a, b) {
|
|
346
608
|
if (!a)
|
|
347
609
|
return false;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository observation contract shared by context recovery and workflow proof.
|
|
3
|
+
*
|
|
4
|
+
* This module has no runtime imports so read-only consumers (the Codec
|
|
5
|
+
* dashboard, proof readers) can use it without reaching the Git-running
|
|
6
|
+
* snapshot code in ./index.ts.
|
|
7
|
+
*/
|
|
8
|
+
export type RepoObservation = "ok" | "unavailable" | "partial";
|
|
9
|
+
export interface RepoSnapshot {
|
|
10
|
+
cwd: string;
|
|
11
|
+
observation: RepoObservation;
|
|
12
|
+
/** Bounded explanation of an unavailable or incomplete observation. */
|
|
13
|
+
observation_detail?: string;
|
|
14
|
+
root?: string;
|
|
15
|
+
branch?: string;
|
|
16
|
+
head?: string;
|
|
17
|
+
dirty_paths: string[];
|
|
18
|
+
dirty_paths_truncated?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Only explicitly successful observations can establish repository drift. */
|
|
21
|
+
export declare function observationsComparable(a: {
|
|
22
|
+
observation?: string;
|
|
23
|
+
}, b: {
|
|
24
|
+
observation?: string;
|
|
25
|
+
}): boolean;
|
|
26
|
+
//# sourceMappingURL=observation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../../../src/core/context/observation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,aAAa,GAAG,SAAS,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,eAAe,CAAC;IAC7B,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,8EAA8E;AAC9E,wBAAgB,sBAAsB,CACpC,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3B,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1B,OAAO,CAET"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repository observation contract shared by context recovery and workflow proof.
|
|
3
|
+
*
|
|
4
|
+
* This module has no runtime imports so read-only consumers (the Codec
|
|
5
|
+
* dashboard, proof readers) can use it without reaching the Git-running
|
|
6
|
+
* snapshot code in ./index.ts.
|
|
7
|
+
*/
|
|
8
|
+
/** Only explicitly successful observations can establish repository drift. */
|
|
9
|
+
export function observationsComparable(a, b) {
|
|
10
|
+
return a.observation === "ok" && b.observation === "ok";
|
|
11
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/core/diagnostics/bundle.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EAEvB,MAAM,uBAAuB,CAAC;AA0B/B,OAAO,EASL,KAAK,wBAAwB,EAG7B,KAAK,sBAAsB,EAE3B,KAAK,iBAAiB,EAItB,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AA2BvB,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,wBAAwB,CAAC;IACnC,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACzD,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,4BAAiC,GACvC,wBAAwB,
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/core/diagnostics/bundle.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,aAAa,EACb,sBAAsB,EAEvB,MAAM,uBAAuB,CAAC;AA0B/B,OAAO,EASL,KAAK,wBAAwB,EAG7B,KAAK,sBAAsB,EAE3B,KAAK,iBAAiB,EAItB,KAAK,yBAAyB,EAC/B,MAAM,eAAe,CAAC;AA2BvB,MAAM,WAAW,4BAA4B;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,wBAAwB,CAAC;IACnC,OAAO,EAAE,iBAAiB,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IACzD,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,4BAAiC,GACvC,wBAAwB,CAwH1B;AAkCD,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,MAAM,GACV,yBAAyB,CAsB3B;AAED,gFAAgF;AAChF,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,yBAAyB,CAK3B;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,yBAAyB,CAE7F;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,uBAAuB,EAAE,CA0BjF;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,4BAA4B,EAAE,CA4C/F;AAqCD,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,EACX,GAAG,OAAa,GACf,sBAAsB,CAgBxB"}
|