@bradheitmann/odin-sentinel 0.4.13 → 0.5.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/.claude-plugin/marketplace.json +1 -1
- package/README.md +24 -17
- package/dist/src/harness-pacing/index.d.ts +10 -0
- package/dist/src/harness-pacing/index.js +11 -0
- package/dist/src/harness-pacing/index.js.map +1 -0
- package/dist/src/harness-pacing/recommend.d.ts +28 -0
- package/dist/src/harness-pacing/recommend.js +74 -0
- package/dist/src/harness-pacing/recommend.js.map +1 -0
- package/dist/src/harness-pacing/schema.d.ts +28 -0
- package/dist/src/harness-pacing/schema.js +2 -0
- package/dist/src/harness-pacing/schema.js.map +1 -0
- package/dist/src/harness-pacing/storage.d.ts +32 -0
- package/dist/src/harness-pacing/storage.js +74 -0
- package/dist/src/harness-pacing/storage.js.map +1 -0
- package/dist/src/mcp/server.js +29 -2
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/odin-watch/backends/cmux.d.ts +6 -0
- package/dist/src/odin-watch/backends/cmux.js +39 -0
- package/dist/src/odin-watch/backends/cmux.js.map +1 -0
- package/dist/src/odin-watch/backends/tmux.d.ts +6 -0
- package/dist/src/odin-watch/backends/tmux.js +40 -0
- package/dist/src/odin-watch/backends/tmux.js.map +1 -0
- package/dist/src/odin-watch/classifier.d.ts +27 -0
- package/dist/src/odin-watch/classifier.js +182 -0
- package/dist/src/odin-watch/classifier.js.map +1 -0
- package/dist/src/odin-watch/index.d.ts +2 -0
- package/dist/src/odin-watch/index.js +200 -0
- package/dist/src/odin-watch/index.js.map +1 -0
- package/dist/src/odin-watch/snapshotter.d.ts +11 -0
- package/dist/src/odin-watch/snapshotter.js +2 -0
- package/dist/src/odin-watch/snapshotter.js.map +1 -0
- package/dist/src/odin-watch/writers.d.ts +8 -0
- package/dist/src/odin-watch/writers.js +27 -0
- package/dist/src/odin-watch/writers.js.map +1 -0
- package/dist/src/protocol/index.d.ts +3 -1
- package/dist/src/protocol/index.js +4 -1
- package/dist/src/protocol/index.js.map +1 -1
- package/dist/src/protocol/repository.d.ts +14 -0
- package/dist/src/protocol/repository.js +25 -1
- package/dist/src/protocol/repository.js.map +1 -1
- package/dist/src/protocol/schemas.d.ts +144 -0
- package/dist/src/protocol/schemas.js +23 -0
- package/dist/src/protocol/schemas.js.map +1 -1
- package/dist/src/protocol/service.d.ts +19 -2
- package/dist/src/protocol/service.js +89 -2
- package/dist/src/protocol/service.js.map +1 -1
- package/dist/src/protocol/surface-layout.d.ts +20 -0
- package/dist/src/protocol/surface-layout.js +20 -0
- package/dist/src/protocol/surface-layout.js.map +1 -1
- package/dist/src/protocol/version.d.ts +2 -2
- package/dist/src/protocol/version.js +2 -2
- package/dist/src/protocol/version.js.map +1 -1
- package/dist/src/utils/execFileNoThrow.d.ts +5 -0
- package/dist/src/utils/execFileNoThrow.js +18 -0
- package/dist/src/utils/execFileNoThrow.js.map +1 -0
- package/docs/adapters/cmux-adapter.md +168 -0
- package/docs/adapters/herdr-adapter.md +150 -0
- package/docs/adapters/minimux-adapter.md +152 -0
- package/docs/adapters/plain-terminal.md +80 -0
- package/docs/adapters/tmux-adapter.md +150 -0
- package/docs/guides/quick-start.md +7 -7
- package/docs/guides/quickstart-prompts.md +4 -4
- package/docs/lattice/odin-lattice-design.md +555 -0
- package/docs/reference/distribution.md +5 -5
- package/docs/reference/public-surface-audit.md +3 -3
- package/package.json +7 -5
- package/plugins/odin-scp/.claude-plugin/plugin.json +2 -2
- package/plugins/odin-scp/README.md +6 -6
- package/plugins/odin-scp/skills/odin-scp/CHANGELOG.md +12 -0
- package/plugins/odin-scp/skills/odin-scp/SKILL.md +196 -3
- package/protocol/SCP.md +2 -2
- package/protocol/bootstrap-skill.md +196 -3
- package/protocol/closeout.yaml +1 -1
- package/protocol/delegation.yaml +1 -1
- package/protocol/mission-frontrun/droids-scrutiny-feature-reviewer.md +70 -0
- package/protocol/mission-frontrun/orchestrator-contract.md +70 -0
- package/protocol/mission-frontrun/scrutiny-feature-reviewer-contract.md +73 -0
- package/protocol/mission-frontrun/scrutiny-validator-contract.md +77 -0
- package/protocol/mission-frontrun/worker-contract.md +66 -0
- package/protocol/model-profiles.yaml +8 -1
- package/protocol/receipts/boot-receipt.yaml +13 -0
- package/protocol/role-cards/dev-worker.md +74 -0
- package/protocol/role-cards/exec-asst.md +83 -0
- package/protocol/role-cards/exec-pm.md +66 -0
- package/protocol/role-cards/qa-worker.md +71 -0
- package/protocol/role-cards/team-pm.md +67 -0
- package/protocol/roles.yaml +1 -1
- package/protocol/topology.yaml +1 -1
- package/scripts/audit/verify-pack.mjs +17 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { execFileNoThrow } from "../../utils/execFileNoThrow.js";
|
|
5
|
+
export class TmuxBackend {
|
|
6
|
+
archiveDir;
|
|
7
|
+
constructor(archiveDir) {
|
|
8
|
+
this.archiveDir = archiveDir;
|
|
9
|
+
}
|
|
10
|
+
async capture(pane_id) {
|
|
11
|
+
const result = await execFileNoThrow("tmux", [
|
|
12
|
+
"capture-pane",
|
|
13
|
+
"-p",
|
|
14
|
+
"-t",
|
|
15
|
+
pane_id,
|
|
16
|
+
]);
|
|
17
|
+
const text = result.status === 0 ? result.stdout : "";
|
|
18
|
+
const ts = Date.now();
|
|
19
|
+
const hash = createHash("sha256").update(text).digest("hex");
|
|
20
|
+
// Archive raw screen text
|
|
21
|
+
try {
|
|
22
|
+
mkdirSync(this.archiveDir, { recursive: true });
|
|
23
|
+
const filename = `${pane_id.replace(/[^a-zA-Z0-9_-]/g, "_")}_${ts}.txt`;
|
|
24
|
+
writeFileSync(join(this.archiveDir, filename), text, {
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Archive failure is non-fatal; snapshot still returned
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
pane_id,
|
|
33
|
+
substrate: "tmux",
|
|
34
|
+
text,
|
|
35
|
+
hash,
|
|
36
|
+
ts,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=tmux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tmux.js","sourceRoot":"","sources":["../../../../src/odin-watch/backends/tmux.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjE,MAAM,OAAO,WAAW;IACL,UAAU,CAAS;IAEpC,YAAY,UAAkB;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QAC3B,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE;YAC3C,cAAc;YACd,IAAI;YACJ,IAAI;YACJ,OAAO;SACR,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACtD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE7D,0BAA0B;QAC1B,IAAI,CAAC;YACH,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC;YACxE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE;gBACnD,QAAQ,EAAE,MAAM;aACjB,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,wDAAwD;QAC1D,CAAC;QAED,OAAO;YACL,OAAO;YACP,SAAS,EAAE,MAAM;YACjB,IAAI;YACJ,IAAI;YACJ,EAAE;SACH,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { WakeVerdict, PromptBudgetClass } from "../protocol/schemas.js";
|
|
2
|
+
export interface ClassifierInput {
|
|
3
|
+
text: string;
|
|
4
|
+
hash: string;
|
|
5
|
+
prev_hash: string | null;
|
|
6
|
+
elapsed_ms: number;
|
|
7
|
+
now_ms: number;
|
|
8
|
+
mandatory_audit_interval_ms: number;
|
|
9
|
+
stale_threshold_ms: number;
|
|
10
|
+
write_scope: string[];
|
|
11
|
+
dirty_paths: string[];
|
|
12
|
+
last_verdict: WakeVerdict | null;
|
|
13
|
+
last_mandatory_audit_ts: number | null;
|
|
14
|
+
}
|
|
15
|
+
export interface ClassifierResult {
|
|
16
|
+
verdict: WakeVerdict;
|
|
17
|
+
wake: 0 | 1;
|
|
18
|
+
reason_codes: string[];
|
|
19
|
+
prompt_budget_class: PromptBudgetClass;
|
|
20
|
+
dirty_out_of_scope: string[];
|
|
21
|
+
last_marker: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Classify a screen snapshot using 8 ordered rules (first-match wins).
|
|
25
|
+
* Pure function — no I/O.
|
|
26
|
+
*/
|
|
27
|
+
export declare function classify(input: ClassifierInput): ClassifierResult;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// Approval markers: exact strings and regex for numbered interactive options
|
|
2
|
+
const APPROVAL_STRINGS = [
|
|
3
|
+
"Proceed with the proposal",
|
|
4
|
+
"Proceed with comment",
|
|
5
|
+
"No and explain why",
|
|
6
|
+
"Enter Select",
|
|
7
|
+
];
|
|
8
|
+
const NUMBERED_OPTION_RE = /^\d+\.\s/m;
|
|
9
|
+
// Crash markers
|
|
10
|
+
const CRASH_STRINGS = [
|
|
11
|
+
"panic",
|
|
12
|
+
"stack trace",
|
|
13
|
+
"program experienced a panic",
|
|
14
|
+
"TUI run error",
|
|
15
|
+
"fatal error",
|
|
16
|
+
];
|
|
17
|
+
// Completion markers (order matters: QA_COMPLETE check before DEV_COMPLETE)
|
|
18
|
+
const QA_COMPLETE_STRINGS = ["QA COMPLETE", "QA_COMPLETE"];
|
|
19
|
+
const DEV_COMPLETE_STRINGS = [
|
|
20
|
+
"DEV_COMPLETE_QA_PENDING",
|
|
21
|
+
"implementation complete",
|
|
22
|
+
"ready for QA",
|
|
23
|
+
];
|
|
24
|
+
// Blocker markers
|
|
25
|
+
const BLOCKER_STRINGS = [
|
|
26
|
+
"BLOCKED",
|
|
27
|
+
"cannot proceed",
|
|
28
|
+
"missing",
|
|
29
|
+
"permission denied",
|
|
30
|
+
"need approval",
|
|
31
|
+
"SPEC-AMENDMENT",
|
|
32
|
+
"non-fast-forward",
|
|
33
|
+
];
|
|
34
|
+
// Working state positive evidence: active spinner chars or streaming progress patterns
|
|
35
|
+
const WORKING_RE = /[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]|(?:\.\.\.|Running|Generating|Compiling|Building|Installing|Fetching|Downloading|Uploading|Processing|Streaming|Writing|Thinking)/;
|
|
36
|
+
function containsAny(text, markers) {
|
|
37
|
+
for (const marker of markers) {
|
|
38
|
+
if (text.includes(marker))
|
|
39
|
+
return marker;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Classify a screen snapshot using 8 ordered rules (first-match wins).
|
|
45
|
+
* Pure function — no I/O.
|
|
46
|
+
*/
|
|
47
|
+
export function classify(input) {
|
|
48
|
+
const { text, hash, prev_hash, elapsed_ms, now_ms, mandatory_audit_interval_ms, stale_threshold_ms, write_scope, dirty_paths, last_verdict, last_mandatory_audit_ts, } = input;
|
|
49
|
+
const dirty_out_of_scope = [];
|
|
50
|
+
// Compute out-of-scope dirty paths (used in rule 6)
|
|
51
|
+
if (write_scope.length > 0) {
|
|
52
|
+
for (const path of dirty_paths) {
|
|
53
|
+
const inScope = write_scope.some((scope) => path === scope || path.startsWith(scope + "/") || path.startsWith(scope));
|
|
54
|
+
if (!inScope) {
|
|
55
|
+
dirty_out_of_scope.push(path);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Rule 1: Mandatory audit due
|
|
60
|
+
// elapsed since last mandatory audit >= interval → wake, preserve current verdict, budget=compact
|
|
61
|
+
const auditElapsed = last_mandatory_audit_ts !== null
|
|
62
|
+
? now_ms - last_mandatory_audit_ts
|
|
63
|
+
: elapsed_ms;
|
|
64
|
+
if (auditElapsed >= mandatory_audit_interval_ms) {
|
|
65
|
+
const preservedVerdict = last_verdict ?? "UNKNOWN_NEEDS_READ";
|
|
66
|
+
return {
|
|
67
|
+
verdict: preservedVerdict,
|
|
68
|
+
wake: 1,
|
|
69
|
+
reason_codes: ["MANDATORY_AUDIT interval elapsed"],
|
|
70
|
+
prompt_budget_class: "compact",
|
|
71
|
+
dirty_out_of_scope,
|
|
72
|
+
last_marker: undefined,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Rule 2: Approval markers
|
|
76
|
+
const approvalMarker = containsAny(text, APPROVAL_STRINGS);
|
|
77
|
+
if (approvalMarker !== null || NUMBERED_OPTION_RE.test(text)) {
|
|
78
|
+
const marker = approvalMarker ?? "numbered interactive option";
|
|
79
|
+
return {
|
|
80
|
+
verdict: "WAITING_APPROVAL",
|
|
81
|
+
wake: 1,
|
|
82
|
+
reason_codes: ["APPROVAL_MARKER"],
|
|
83
|
+
prompt_budget_class: "diagnostic",
|
|
84
|
+
dirty_out_of_scope,
|
|
85
|
+
last_marker: marker,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// Rule 3: Crash markers
|
|
89
|
+
const crashMarker = containsAny(text, CRASH_STRINGS);
|
|
90
|
+
if (crashMarker !== null) {
|
|
91
|
+
return {
|
|
92
|
+
verdict: "CRASHED",
|
|
93
|
+
wake: 1,
|
|
94
|
+
reason_codes: ["CRASH_MARKER"],
|
|
95
|
+
prompt_budget_class: "forensic",
|
|
96
|
+
dirty_out_of_scope,
|
|
97
|
+
last_marker: crashMarker,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Rule 4: Completion markers (QA_COMPLETE checked before DEV_COMPLETE)
|
|
101
|
+
const qaMarker = containsAny(text, QA_COMPLETE_STRINGS);
|
|
102
|
+
if (qaMarker !== null) {
|
|
103
|
+
return {
|
|
104
|
+
verdict: "QA_COMPLETE",
|
|
105
|
+
wake: 1,
|
|
106
|
+
reason_codes: ["COMPLETION_MARKER"],
|
|
107
|
+
prompt_budget_class: "compact",
|
|
108
|
+
dirty_out_of_scope,
|
|
109
|
+
last_marker: qaMarker,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const devMarker = containsAny(text, DEV_COMPLETE_STRINGS);
|
|
113
|
+
if (devMarker !== null) {
|
|
114
|
+
return {
|
|
115
|
+
verdict: "DEV_COMPLETE_QA_PENDING",
|
|
116
|
+
wake: 1,
|
|
117
|
+
reason_codes: ["COMPLETION_MARKER"],
|
|
118
|
+
prompt_budget_class: "compact",
|
|
119
|
+
dirty_out_of_scope,
|
|
120
|
+
last_marker: devMarker,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Rule 5: Blocker markers
|
|
124
|
+
const blockerMarker = containsAny(text, BLOCKER_STRINGS);
|
|
125
|
+
if (blockerMarker !== null) {
|
|
126
|
+
return {
|
|
127
|
+
verdict: "BLOCKED",
|
|
128
|
+
wake: 1,
|
|
129
|
+
reason_codes: ["BLOCKER_MARKER"],
|
|
130
|
+
prompt_budget_class: "diagnostic",
|
|
131
|
+
dirty_out_of_scope,
|
|
132
|
+
last_marker: blockerMarker,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Rule 6: Scope violation (dirty paths outside write_scope)
|
|
136
|
+
if (dirty_out_of_scope.length > 0) {
|
|
137
|
+
return {
|
|
138
|
+
verdict: "BLOCKED",
|
|
139
|
+
wake: 1,
|
|
140
|
+
reason_codes: dirty_out_of_scope.map((p) => `DIRTY_OUT_OF_SCOPE ${p}`),
|
|
141
|
+
prompt_budget_class: "diagnostic",
|
|
142
|
+
dirty_out_of_scope,
|
|
143
|
+
last_marker: undefined,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// Rule 7: Stale hash (unchanged content beyond stale threshold)
|
|
147
|
+
if (prev_hash !== null &&
|
|
148
|
+
prev_hash === hash &&
|
|
149
|
+
elapsed_ms > stale_threshold_ms) {
|
|
150
|
+
return {
|
|
151
|
+
verdict: "IDLE",
|
|
152
|
+
wake: 1,
|
|
153
|
+
reason_codes: ["STALE_HASH"],
|
|
154
|
+
prompt_budget_class: "compact",
|
|
155
|
+
dirty_out_of_scope,
|
|
156
|
+
last_marker: undefined,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// Rule 8: All clear — WORKING requires positive evidence
|
|
160
|
+
// Positive evidence: text contains active working indicators
|
|
161
|
+
if (WORKING_RE.test(text)) {
|
|
162
|
+
return {
|
|
163
|
+
verdict: "WORKING",
|
|
164
|
+
wake: 0,
|
|
165
|
+
reason_codes: ["ALL_CLEAR"],
|
|
166
|
+
prompt_budget_class: "silent",
|
|
167
|
+
dirty_out_of_scope,
|
|
168
|
+
last_marker: undefined,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// Conservative bias: ambiguous (empty text with no stale trigger, or non-empty
|
|
172
|
+
// without positive WORKING evidence) → UNKNOWN_NEEDS_READ, wake=1
|
|
173
|
+
return {
|
|
174
|
+
verdict: "UNKNOWN_NEEDS_READ",
|
|
175
|
+
wake: 1,
|
|
176
|
+
reason_codes: ["AMBIGUOUS_STATE"],
|
|
177
|
+
prompt_budget_class: "compact",
|
|
178
|
+
dirty_out_of_scope,
|
|
179
|
+
last_marker: undefined,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../../src/odin-watch/classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from "node:util";
|
|
3
|
+
import { CmuxBackend } from "./backends/cmux.js";
|
|
4
|
+
import { TmuxBackend } from "./backends/tmux.js";
|
|
5
|
+
import { classify } from "./classifier.js";
|
|
6
|
+
import { writeWakeFiles } from "./writers.js";
|
|
7
|
+
import { execFileNoThrow } from "../utils/execFileNoThrow.js";
|
|
8
|
+
const VALID_SUBSTRATES = [
|
|
9
|
+
"cmux",
|
|
10
|
+
"tmux",
|
|
11
|
+
"minimux",
|
|
12
|
+
"herdr",
|
|
13
|
+
"plain",
|
|
14
|
+
];
|
|
15
|
+
function isValidSubstrate(s) {
|
|
16
|
+
return VALID_SUBSTRATES.includes(s);
|
|
17
|
+
}
|
|
18
|
+
function parseCliArgs() {
|
|
19
|
+
const { values } = parseArgs({
|
|
20
|
+
options: {
|
|
21
|
+
surface: { type: "string" },
|
|
22
|
+
substrate: { type: "string" },
|
|
23
|
+
"write-scope": { type: "string", multiple: true },
|
|
24
|
+
interval: { type: "string", default: "120" },
|
|
25
|
+
"mandatory-audit": { type: "string", default: "600" },
|
|
26
|
+
"flag-dir": {
|
|
27
|
+
type: "string",
|
|
28
|
+
default: ".odin/monitor/wake/",
|
|
29
|
+
},
|
|
30
|
+
"archive-dir": {
|
|
31
|
+
type: "string",
|
|
32
|
+
default: ".odin/monitor/cmux/",
|
|
33
|
+
},
|
|
34
|
+
label: { type: "string" },
|
|
35
|
+
},
|
|
36
|
+
strict: true,
|
|
37
|
+
});
|
|
38
|
+
const surface = values["surface"];
|
|
39
|
+
const substrate = values["substrate"];
|
|
40
|
+
const label = values["label"];
|
|
41
|
+
if (!surface) {
|
|
42
|
+
console.error("Error: --surface <id> is required");
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
if (!substrate) {
|
|
46
|
+
console.error("Error: --substrate <type> is required");
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
if (!isValidSubstrate(substrate)) {
|
|
50
|
+
console.error(`Error: --substrate must be one of: ${VALID_SUBSTRATES.join(", ")}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
if (!label) {
|
|
54
|
+
console.error("Error: --label <name> is required");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const intervalSeconds = parseInt(values["interval"] ?? "120", 10);
|
|
58
|
+
const mandatoryAuditSeconds = parseInt(values["mandatory-audit"] ?? "600", 10);
|
|
59
|
+
if (isNaN(intervalSeconds) || intervalSeconds <= 0) {
|
|
60
|
+
console.error("Error: --interval must be a positive integer");
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
if (isNaN(mandatoryAuditSeconds) || mandatoryAuditSeconds <= 0) {
|
|
64
|
+
console.error("Error: --mandatory-audit must be a positive integer");
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
surface,
|
|
69
|
+
substrate,
|
|
70
|
+
writeScope: values["write-scope"] ?? [],
|
|
71
|
+
intervalSeconds,
|
|
72
|
+
mandatoryAuditSeconds,
|
|
73
|
+
flagDir: values["flag-dir"] ?? ".odin/monitor/wake/",
|
|
74
|
+
archiveDir: values["archive-dir"] ?? ".odin/monitor/cmux/",
|
|
75
|
+
label,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
async function getDirtyPaths() {
|
|
79
|
+
const result = await execFileNoThrow("git", [
|
|
80
|
+
"diff",
|
|
81
|
+
"--name-only",
|
|
82
|
+
"--diff-filter=ACDMRT",
|
|
83
|
+
]);
|
|
84
|
+
if (result.status !== 0)
|
|
85
|
+
return [];
|
|
86
|
+
return result.stdout
|
|
87
|
+
.split("\n")
|
|
88
|
+
.map((l) => l.trim())
|
|
89
|
+
.filter(Boolean);
|
|
90
|
+
}
|
|
91
|
+
async function getCurrentBranch() {
|
|
92
|
+
const result = await execFileNoThrow("git", [
|
|
93
|
+
"rev-parse",
|
|
94
|
+
"--abbrev-ref",
|
|
95
|
+
"HEAD",
|
|
96
|
+
]);
|
|
97
|
+
return result.status === 0 ? result.stdout.trim() : undefined;
|
|
98
|
+
}
|
|
99
|
+
async function getCurrentHead() {
|
|
100
|
+
const result = await execFileNoThrow("git", ["rev-parse", "--short", "HEAD"]);
|
|
101
|
+
return result.status === 0 ? result.stdout.trim() : undefined;
|
|
102
|
+
}
|
|
103
|
+
async function sleep(ms) {
|
|
104
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
105
|
+
}
|
|
106
|
+
async function main() {
|
|
107
|
+
const { surface, substrate, writeScope, intervalSeconds, mandatoryAuditSeconds, flagDir, archiveDir, label, } = parseCliArgs();
|
|
108
|
+
const intervalMs = intervalSeconds * 1000;
|
|
109
|
+
const mandatoryAuditIntervalMs = mandatoryAuditSeconds * 1000;
|
|
110
|
+
// Select backend based on substrate
|
|
111
|
+
let snapshotter;
|
|
112
|
+
switch (substrate) {
|
|
113
|
+
case "cmux":
|
|
114
|
+
snapshotter = new CmuxBackend(archiveDir);
|
|
115
|
+
break;
|
|
116
|
+
case "tmux":
|
|
117
|
+
snapshotter = new TmuxBackend(archiveDir);
|
|
118
|
+
break;
|
|
119
|
+
default:
|
|
120
|
+
throw new Error(`Substrate '${substrate}' is not yet supported by odin-watch. Supported: cmux, tmux`);
|
|
121
|
+
}
|
|
122
|
+
let prevHash = null;
|
|
123
|
+
let lastVerdictState = null;
|
|
124
|
+
let lastMandatoryAuditTs = null;
|
|
125
|
+
const startTs = Date.now();
|
|
126
|
+
console.error(`odin-watch starting: surface=${surface} substrate=${substrate} label=${label} interval=${intervalSeconds}s`);
|
|
127
|
+
while (true) {
|
|
128
|
+
const loopStart = Date.now();
|
|
129
|
+
const elapsed = loopStart - startTs;
|
|
130
|
+
try {
|
|
131
|
+
// Capture
|
|
132
|
+
const snapshot = await snapshotter.capture(surface);
|
|
133
|
+
// Gather dirty paths and git metadata
|
|
134
|
+
const [dirtyPaths, branch, head] = await Promise.all([
|
|
135
|
+
getDirtyPaths(),
|
|
136
|
+
getCurrentBranch(),
|
|
137
|
+
getCurrentHead(),
|
|
138
|
+
]);
|
|
139
|
+
// Classify
|
|
140
|
+
const result = classify({
|
|
141
|
+
text: snapshot.text,
|
|
142
|
+
hash: snapshot.hash,
|
|
143
|
+
prev_hash: prevHash,
|
|
144
|
+
elapsed_ms: elapsed,
|
|
145
|
+
now_ms: Date.now(),
|
|
146
|
+
mandatory_audit_interval_ms: mandatoryAuditIntervalMs,
|
|
147
|
+
stale_threshold_ms: intervalMs * 3, // stale after 3 missed polls
|
|
148
|
+
write_scope: writeScope,
|
|
149
|
+
dirty_paths: dirtyPaths,
|
|
150
|
+
last_verdict: lastVerdictState,
|
|
151
|
+
last_mandatory_audit_ts: lastMandatoryAuditTs,
|
|
152
|
+
});
|
|
153
|
+
// If mandatory audit triggered, reset the audit clock
|
|
154
|
+
if (result.reason_codes.includes("MANDATORY_AUDIT interval elapsed")) {
|
|
155
|
+
lastMandatoryAuditTs = loopStart;
|
|
156
|
+
}
|
|
157
|
+
// Build WakeState
|
|
158
|
+
const wakeState = {
|
|
159
|
+
schema_version: "1.0",
|
|
160
|
+
ts: new Date(loopStart).toISOString(),
|
|
161
|
+
surface,
|
|
162
|
+
pane_id: surface,
|
|
163
|
+
substrate: substrate,
|
|
164
|
+
state: result.verdict,
|
|
165
|
+
wake: result.wake,
|
|
166
|
+
reason_codes: result.reason_codes,
|
|
167
|
+
dirty_paths: dirtyPaths,
|
|
168
|
+
dirty_out_of_scope: result.dirty_out_of_scope,
|
|
169
|
+
screen_hash: snapshot.hash,
|
|
170
|
+
screen_changed: prevHash !== null && prevHash !== snapshot.hash,
|
|
171
|
+
last_marker: result.last_marker,
|
|
172
|
+
prompt_budget_class: result.prompt_budget_class,
|
|
173
|
+
next_mandatory_audit_due: new Date(loopStart + mandatoryAuditIntervalMs).toISOString(),
|
|
174
|
+
head,
|
|
175
|
+
branch,
|
|
176
|
+
};
|
|
177
|
+
// Write output files
|
|
178
|
+
writeWakeFiles(flagDir, label, wakeState);
|
|
179
|
+
prevHash = snapshot.hash;
|
|
180
|
+
lastVerdictState = result.verdict;
|
|
181
|
+
console.error(`[${wakeState.ts}] verdict=${result.verdict} wake=${result.wake} hash=${snapshot.hash.slice(0, 8)}`);
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
185
|
+
console.error(`odin-watch poll error: ${msg}`);
|
|
186
|
+
}
|
|
187
|
+
// Sleep until next interval
|
|
188
|
+
const elapsed2 = Date.now() - loopStart;
|
|
189
|
+
const remaining = intervalMs - elapsed2;
|
|
190
|
+
if (remaining > 0) {
|
|
191
|
+
await sleep(remaining);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
main().catch((err) => {
|
|
196
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
197
|
+
console.error(`odin-watch fatal: ${msg}`);
|
|
198
|
+
process.exitCode = 1;
|
|
199
|
+
});
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/odin-watch/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SubstrateType } from "../protocol/schemas.js";
|
|
2
|
+
export interface Snapshot {
|
|
3
|
+
pane_id: string;
|
|
4
|
+
substrate: SubstrateType;
|
|
5
|
+
text: string;
|
|
6
|
+
hash: string;
|
|
7
|
+
ts: number;
|
|
8
|
+
}
|
|
9
|
+
export interface Snapshotter {
|
|
10
|
+
capture(pane_id: string): Promise<Snapshot>;
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotter.js","sourceRoot":"","sources":["../../../src/odin-watch/snapshotter.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { WakeState } from "../protocol/schemas.js";
|
|
2
|
+
/**
|
|
3
|
+
* Write the three output files for a single poll result:
|
|
4
|
+
* {flagDir}/{label}.flag — exactly one byte: "0" or "1", no newline
|
|
5
|
+
* {flagDir}/{label}.reason — text reason codes, one per line
|
|
6
|
+
* {flagDir}/{label}.state.json — WakeState JSON object
|
|
7
|
+
*/
|
|
8
|
+
export declare function writeWakeFiles(flagDir: string, label: string, state: WakeState): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Write the three output files for a single poll result:
|
|
5
|
+
* {flagDir}/{label}.flag — exactly one byte: "0" or "1", no newline
|
|
6
|
+
* {flagDir}/{label}.reason — text reason codes, one per line
|
|
7
|
+
* {flagDir}/{label}.state.json — WakeState JSON object
|
|
8
|
+
*/
|
|
9
|
+
export function writeWakeFiles(flagDir, label, state) {
|
|
10
|
+
mkdirSync(flagDir, { recursive: true });
|
|
11
|
+
// Flag file: exactly one byte ASCII character "0" or "1", no newline
|
|
12
|
+
const flagPath = join(flagDir, `${label}.flag`);
|
|
13
|
+
writeFileSync(flagPath, String(state.wake), {
|
|
14
|
+
encoding: "utf8",
|
|
15
|
+
});
|
|
16
|
+
// Reason file: one reason code per line
|
|
17
|
+
const reasonPath = join(flagDir, `${label}.reason`);
|
|
18
|
+
writeFileSync(reasonPath, state.reason_codes.join("\n"), {
|
|
19
|
+
encoding: "utf8",
|
|
20
|
+
});
|
|
21
|
+
// State JSON file
|
|
22
|
+
const statePath = join(flagDir, `${label}.state.json`);
|
|
23
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2), {
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=writers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writers.js","sourceRoot":"","sources":["../../../src/odin-watch/writers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,KAAa,EACb,KAAgB;IAEhB,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExC,qEAAqE;IACrE,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC;IAChD,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAc,EAAE;QACvD,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,wCAAwC;IACxC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC;IACpD,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QACvD,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IAEH,kBAAkB;IAClB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,KAAK,aAAa,CAAC,CAAC;IACvD,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QACvD,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { GOVERNED_CONTEXT_PROOF_SCHEMA, VERSION, classifyGovernedReadiness, createProtocolService, exportProtocolSnapshot, getActivationGates, getCloseoutChecklist, getDelegationPacket, getOnboardingPlan, getRoleProfile, getRuntimeNotice, getStartupPacket, harnessCategory, validateBootReceipt, validateCmuxDeliveryProof, validateDelegationPacket, validateGovernedContextProof, validateInstructionReadProof, validateTeamManifest } from "./service.js";
|
|
2
2
|
export type { AssuranceLevel, CloseoutMode, DelegationPacketInput, GovernedReadinessInput, GovernedReadinessResult, GovernedReadinessState, HarnessCategory, ProtocolData, RuntimeNotice, StartupPacket, StartupPacketInput, ValidationResult } from "./service.js";
|
|
3
|
-
export { computeSurfaceLayout, computeSurfaceLayoutGate, renderSurfaceLayoutAscii } from "./surface-layout.js";
|
|
3
|
+
export { computeSurfaceLayout, computeSurfaceLayoutGate, getSubstrateCapability, renderSurfaceLayoutAscii } from "./surface-layout.js";
|
|
4
4
|
export type { SurfaceLayout, SurfaceLayoutColumn, SurfaceLayoutSurface, SurfaceLayoutGate } from "./surface-layout.js";
|
|
5
|
+
export { CapabilityFlag } from "./schemas.js";
|
|
6
|
+
export type { BootReceipt, DeliveryReceiptPacingExtension, HarnessPacingEvent, PacingEventType, PromptBudgetClass, ReceiptType, SubstrateCapability, SubstrateType, WakeState, WakeVerdict } from "./schemas.js";
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export { GOVERNED_CONTEXT_PROOF_SCHEMA, VERSION, classifyGovernedReadiness, createProtocolService, exportProtocolSnapshot, getActivationGates, getCloseoutChecklist, getDelegationPacket, getOnboardingPlan, getRoleProfile, getRuntimeNotice, getStartupPacket, harnessCategory, validateBootReceipt, validateCmuxDeliveryProof, validateDelegationPacket, validateGovernedContextProof, validateInstructionReadProof, validateTeamManifest } from "./service.js";
|
|
2
|
-
export { computeSurfaceLayout, computeSurfaceLayoutGate, renderSurfaceLayoutAscii } from "./surface-layout.js";
|
|
2
|
+
export { computeSurfaceLayout, computeSurfaceLayoutGate, getSubstrateCapability, renderSurfaceLayoutAscii } from "./surface-layout.js";
|
|
3
|
+
// New types for SCP performance/portability program (E1-E5 unblocking)
|
|
4
|
+
// CapabilityFlag is exported as a value (runtime const) and as a type via the same export.
|
|
5
|
+
export { CapabilityFlag } from "./schemas.js";
|
|
3
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/protocol/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,OAAO,EACP,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,EAC5B,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAiBtB,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/protocol/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,OAAO,EACP,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,4BAA4B,EAC5B,oBAAoB,EACrB,MAAM,cAAc,CAAC;AAiBtB,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAS7B,uEAAuE;AACvE,2FAA2F;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -15,6 +15,20 @@ export type ProtocolData = {
|
|
|
15
15
|
harnessSkillTargets: string;
|
|
16
16
|
teamBootstrapRunbook: string;
|
|
17
17
|
};
|
|
18
|
+
roleCards: {
|
|
19
|
+
execPm: string;
|
|
20
|
+
teamPm: string;
|
|
21
|
+
devWorker: string;
|
|
22
|
+
qaWorker: string;
|
|
23
|
+
execAsst: string;
|
|
24
|
+
};
|
|
25
|
+
missionFrontrun: {
|
|
26
|
+
orchestratorContract: string;
|
|
27
|
+
workerContract: string;
|
|
28
|
+
scrutinyValidatorContract: string;
|
|
29
|
+
scrutinyFeatureReviewerContract: string;
|
|
30
|
+
droidsScrutinyFeatureReviewer: string;
|
|
31
|
+
};
|
|
18
32
|
};
|
|
19
33
|
export type ProtocolRepository = {
|
|
20
34
|
load(): ProtocolData;
|
|
@@ -17,7 +17,17 @@ export const REQUIRED_PROTOCOL_FILES = [
|
|
|
17
17
|
"protocol/skill-references/boot-receipt-examples.md",
|
|
18
18
|
"protocol/skill-references/canonical-introduction-prompt.md",
|
|
19
19
|
"protocol/skill-references/harness-skill-targets.md",
|
|
20
|
-
"protocol/skill-references/team-bootstrap-runbook.md"
|
|
20
|
+
"protocol/skill-references/team-bootstrap-runbook.md",
|
|
21
|
+
"protocol/role-cards/exec-pm.md",
|
|
22
|
+
"protocol/role-cards/team-pm.md",
|
|
23
|
+
"protocol/role-cards/dev-worker.md",
|
|
24
|
+
"protocol/role-cards/qa-worker.md",
|
|
25
|
+
"protocol/role-cards/exec-asst.md",
|
|
26
|
+
"protocol/mission-frontrun/orchestrator-contract.md",
|
|
27
|
+
"protocol/mission-frontrun/worker-contract.md",
|
|
28
|
+
"protocol/mission-frontrun/scrutiny-validator-contract.md",
|
|
29
|
+
"protocol/mission-frontrun/scrutiny-feature-reviewer-contract.md",
|
|
30
|
+
"protocol/mission-frontrun/droids-scrutiny-feature-reviewer.md"
|
|
21
31
|
];
|
|
22
32
|
function missingProtocolFiles(root) {
|
|
23
33
|
return REQUIRED_PROTOCOL_FILES.filter((file) => !existsSync(join(root, file)));
|
|
@@ -105,6 +115,20 @@ export function createFileProtocolRepository(rootDir = findRootDir()) {
|
|
|
105
115
|
canonicalIntroductionPrompt: readFileSync(protocolPath("skill-references", "canonical-introduction-prompt.md"), "utf8"),
|
|
106
116
|
harnessSkillTargets: readFileSync(protocolPath("skill-references", "harness-skill-targets.md"), "utf8"),
|
|
107
117
|
teamBootstrapRunbook: readFileSync(protocolPath("skill-references", "team-bootstrap-runbook.md"), "utf8")
|
|
118
|
+
},
|
|
119
|
+
roleCards: {
|
|
120
|
+
execPm: readFileSync(protocolPath("role-cards", "exec-pm.md"), "utf8"),
|
|
121
|
+
teamPm: readFileSync(protocolPath("role-cards", "team-pm.md"), "utf8"),
|
|
122
|
+
devWorker: readFileSync(protocolPath("role-cards", "dev-worker.md"), "utf8"),
|
|
123
|
+
qaWorker: readFileSync(protocolPath("role-cards", "qa-worker.md"), "utf8"),
|
|
124
|
+
execAsst: readFileSync(protocolPath("role-cards", "exec-asst.md"), "utf8")
|
|
125
|
+
},
|
|
126
|
+
missionFrontrun: {
|
|
127
|
+
orchestratorContract: readFileSync(protocolPath("mission-frontrun", "orchestrator-contract.md"), "utf8"),
|
|
128
|
+
workerContract: readFileSync(protocolPath("mission-frontrun", "worker-contract.md"), "utf8"),
|
|
129
|
+
scrutinyValidatorContract: readFileSync(protocolPath("mission-frontrun", "scrutiny-validator-contract.md"), "utf8"),
|
|
130
|
+
scrutinyFeatureReviewerContract: readFileSync(protocolPath("mission-frontrun", "scrutiny-feature-reviewer-contract.md"), "utf8"),
|
|
131
|
+
droidsScrutinyFeatureReviewer: readFileSync(protocolPath("mission-frontrun", "droids-scrutiny-feature-reviewer.md"), "utf8")
|
|
108
132
|
}
|
|
109
133
|
});
|
|
110
134
|
cache = { fingerprint, data };
|