dsh-context-memory 0.7.0 → 0.9.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.
@@ -10,6 +10,7 @@ const RECALL_PATH = '/api/context-memory.recall';
10
10
  const REMEMBER_PATH = '/api/context-memory.remember';
11
11
  const FORGET_PATH = '/api/context-memory.forget';
12
12
  const PREFS_PATH = '/api/context-memory.prefs';
13
+ const CANDIDATES_PATH = '/api/context-memory.candidates';
13
14
  export function checkLabel(memory, t) {
14
15
  if (!memory.guard)
15
16
  return t('checkNone');
@@ -21,7 +22,14 @@ export function checkLabel(memory, t) {
21
22
  }
22
23
  }
23
24
  export function provenance(memory) {
24
- return [memory.source, memory.sessionId !== undefined ? `session ${memory.sessionId}` : null, memory.step !== undefined ? `step ${String(memory.step)}` : null].filter((part) => part !== null).join(' · ');
25
+ // Scope first: the same sentence can be held in both logs with the same id, so without
26
+ // it two rows would read identically and the "forget" button beside them would be a
27
+ // coin flip over which log it retires.
28
+ return [memory.scope, memory.source, memory.sessionId !== undefined ? `session ${memory.sessionId}` : null, memory.step !== undefined ? `step ${String(memory.step)}` : null].filter((part) => part !== null).join(' · ');
29
+ }
30
+ /** A row key that is unique across scopes, where the id alone is not. */
31
+ export function memoryKey(memory) {
32
+ return `${memory.scope}:${memory.id}`;
25
33
  }
26
34
  function AddMemoryModal({ t, onClose, onAdded }) {
27
35
  const toast = useToast();
@@ -89,13 +97,20 @@ function ContextMemoryPanelInner({ t }) {
89
97
  reload();
90
98
  }
91
99
  }, [forgetTarget, t, toast, reload]);
100
+ const decideCandidate = useCallback(async (id, action) => {
101
+ const r = await fetch(CANDIDATES_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, action }) });
102
+ if (r.ok) {
103
+ toast('success', action === 'accept' ? t('candidateAccepted') : t('candidateDismissed'));
104
+ reload();
105
+ }
106
+ }, [t, toast, reload]);
92
107
  const header = (_jsxs("header", { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 4 }, children: [_jsxs("strong", { style: { fontSize: 15 }, children: ["\uD83E\uDDE0 ", t('title')] }), _jsx("span", { style: { flex: 1 } }), _jsxs(Button, { variant: "primary", size: "sm", onClick: () => setAddOpen(true), children: ["\u2795 ", t('addMemory')] }), _jsx(Button, { variant: "secondary", size: "sm", onClick: reload, children: t('refresh') })] }));
93
108
  if (error !== null)
94
109
  return _jsxs("div", { style: { maxWidth: 820 }, children: [header, _jsx(Card, { children: _jsxs("p", { role: "alert", style: { margin: 0, fontSize: 13, color: 'var(--dsw-alias-state-error-primary)' }, children: [t('failed'), ": ", error] }) })] });
95
110
  if (payload === null)
96
111
  return _jsxs("div", { style: { maxWidth: 820 }, children: [header, _jsx("div", { style: { display: 'flex', justifyContent: 'center', padding: 40 }, children: _jsx(Spinner, { size: 28 }) })] });
97
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }, children: [header, _jsxs("div", { style: { display: 'flex', gap: 10, flexWrap: 'wrap' }, children: [_jsx(StatCard, { value: payload.totals.active, label: t('active') }), _jsx(StatCard, { value: payload.totals.verified, label: t('checkPassed') }), _jsx(StatCard, { value: payload.totals.unverified, label: t('unverified') }), _jsx(StatCard, { value: payload.totals.superseded, label: t('superseded') })] }), _jsx(Card, { title: t('integration'), icon: "\u2699\uFE0F", children: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [['autoCapture', 'autoCaptureLabel'], ['recallInject', 'recallInjectLabel']].map(([key, labelKey]) => (_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }, children: [_jsx("input", { type: "checkbox", checked: prefs?.[key] ?? true, onChange: () => togglePref(key) }), t(labelKey)] }, key))) }) }), _jsxs(Card, { title: t('search'), icon: "\uD83D\uDD0D", children: [_jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('searchPlaceholder'), onKeyDown: (e) => { if (e.key === 'Enter')
98
- handleSearch(); } }), _jsxs(Button, { variant: "secondary", onClick: handleSearch, disabled: searching, children: [searching ? _jsx(Spinner, { size: 14 }) : null, " ", t('search')] })] }), searchHits !== null && (_jsx("div", { style: { marginTop: 10 }, children: searchHits.length === 0 ? _jsx(EmptyState, { message: t('noResults') }) : (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: searchHits.map((hit) => (_jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'baseline' }, children: [_jsx(Badge, { color: "info", children: hit.score.toFixed(2) }), _jsx("span", { style: { fontSize: 12 }, children: hit.memory.text }), _jsxs("span", { style: { fontSize: 10, opacity: 0.5 }, children: ["\u2014 ", hit.why] })] }, hit.memory.id))) })) }))] }), payload.conflicts.length > 0 && (_jsxs(_Fragment, { children: [_jsx(SectionTitle, { icon: "\u26A0\uFE0F", children: t('conflicts') }), payload.conflicts.map((conflict) => (_jsx(Card, { title: conflict.subject, children: conflict.memories.map((memory) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }, children: [_jsx("code", { style: { fontSize: 11 }, children: memory.id }), _jsx("span", { style: { fontSize: 12, flex: 1 }, children: memory.text }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, reason: `conflict resolved: ${conflict.subject}` }), children: t('resolve') })] }, memory.id))) }, conflict.subject)))] })), _jsx(SectionTitle, { icon: "\u2705", children: t('active') }), payload.active.length === 0 ? _jsx(EmptyState, { icon: "\uD83D\uDCED", message: t('empty') }) : (_jsx(Card, { padding: 0, children: _jsxs("table", { style: tableStyles.table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableStyles.th, children: "Id" }), _jsx("th", { style: tableStyles.th, children: t('memory') }), _jsx("th", { style: tableStyles.th, children: t('kind') }), _jsx("th", { style: tableStyles.th, children: t('status') }), _jsx("th", { style: tableStyles.th, children: t('from') }), _jsx("th", { style: tableStyles.th })] }) }), _jsx("tbody", { children: payload.active.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: tableStyles.td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: tableStyles.td, children: memory.text }), _jsx("td", { style: tableStyles.td, children: _jsx(Badge, { children: memory.kind }) }), _jsx("td", { style: tableStyles.td, children: checkLabel(memory, t) }), _jsx("td", { style: { ...tableStyles.td, fontSize: 11, opacity: 0.6 }, children: provenance(memory) }), _jsx("td", { style: tableStyles.td, children: _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, reason: 'superseded from panel' }), children: "\uD83D\uDDD1" }) })] }, memory.id))) })] }) })), payload.superseded.length > 0 && (_jsxs(_Fragment, { children: [_jsxs(SectionTitle, { icon: "\uD83D\uDCE6", children: [t('superseded'), " (", payload.superseded.length, ")"] }), _jsx(Card, { padding: 0, children: _jsxs("table", { style: tableStyles.table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableStyles.th, children: "Id" }), _jsx("th", { style: tableStyles.th, children: t('memory') }), _jsx("th", { style: tableStyles.th, children: t('kind') }), _jsx("th", { style: tableStyles.th, children: t('status') })] }) }), _jsx("tbody", { children: payload.superseded.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: tableStyles.td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: tableStyles.td, children: memory.text }), _jsx("td", { style: tableStyles.td, children: _jsx(Badge, { children: memory.kind }) }), _jsx("td", { style: tableStyles.td, children: checkLabel(memory, t) })] }, memory.id))) })] }) })] })), addOpen && _jsx(AddMemoryModal, { t: t, onClose: () => setAddOpen(false), onAdded: reload }), forgetTarget !== null && _jsx(ConfirmDialog, { title: t('forget'), message: t('confirmForget'), confirmLabel: t('forget'), danger: true, onConfirm: handleForget, onClose: () => setForgetTarget(null) })] }));
112
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }, children: [header, _jsxs("div", { style: { display: 'flex', gap: 10, flexWrap: 'wrap' }, children: [_jsx(StatCard, { value: payload.totals.active, label: t('active') }), _jsx(StatCard, { value: payload.totals.verified, label: t('checkPassed') }), _jsx(StatCard, { value: payload.totals.unverified, label: t('unverified') }), _jsx(StatCard, { value: payload.totals.superseded, label: t('superseded') }), payload.totals.candidates > 0 && _jsx(StatCard, { value: payload.totals.candidates, label: t('candidates') })] }), _jsx(Card, { title: t('integration'), icon: "\u2699\uFE0F", children: _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [['autoCapture', 'autoCaptureLabel'], ['recallInject', 'recallInjectLabel']].map(([key, labelKey]) => (_jsxs("label", { style: { display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, cursor: 'pointer' }, children: [_jsx("input", { type: "checkbox", checked: prefs?.[key] ?? true, onChange: () => togglePref(key) }), t(labelKey)] }, key))) }) }), _jsxs(Card, { title: t('search'), icon: "\uD83D\uDD0D", children: [_jsxs("div", { style: { display: 'flex', gap: 8 }, children: [_jsx(Input, { value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), placeholder: t('searchPlaceholder'), onKeyDown: (e) => { if (e.key === 'Enter')
113
+ handleSearch(); } }), _jsxs(Button, { variant: "secondary", onClick: handleSearch, disabled: searching, children: [searching ? _jsx(Spinner, { size: 14 }) : null, " ", t('search')] })] }), searchHits !== null && (_jsx("div", { style: { marginTop: 10 }, children: searchHits.length === 0 ? _jsx(EmptyState, { message: t('noResults') }) : (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: searchHits.map((hit) => (_jsxs("div", { style: { display: 'flex', gap: 8, alignItems: 'baseline' }, children: [_jsx(Badge, { color: "info", children: hit.score.toFixed(2) }), _jsx("span", { style: { fontSize: 12 }, children: hit.memory.text }), _jsxs("span", { style: { fontSize: 10, opacity: 0.5 }, children: ["\u2014 ", hit.why] })] }, hit.memory.id))) })) }))] }), payload.conflicts.length > 0 && (_jsxs(_Fragment, { children: [_jsx(SectionTitle, { icon: "\u26A0\uFE0F", children: t('conflicts') }), payload.conflicts.map((conflict) => (_jsx(Card, { title: conflict.subject, children: conflict.memories.map((memory) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }, children: [_jsx("code", { style: { fontSize: 11 }, children: memory.id }), _jsx("span", { style: { fontSize: 12, flex: 1 }, children: memory.text }), _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, reason: `conflict resolved: ${conflict.subject}` }), children: t('resolve') })] }, memory.id))) }, conflict.subject)))] })), payload.candidates.length > 0 && (_jsxs(_Fragment, { children: [_jsxs(SectionTitle, { icon: "\uD83D\uDCA1", children: [t('candidates'), " (", payload.candidates.length, ")"] }), _jsxs(Card, { children: [_jsx("p", { style: { margin: '0 0 10px', fontSize: 12, opacity: 0.7 }, children: t('candidatesHint') }), _jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: payload.candidates.map((candidate) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx(Badge, { children: candidate.kind }), _jsx("span", { style: { fontSize: 12, flex: 1 }, children: candidate.text }), _jsx(Button, { variant: "primary", size: "sm", onClick: () => void decideCandidate(candidate.id, 'accept'), children: t('accept') }), _jsx(Button, { variant: "secondary", size: "sm", onClick: () => void decideCandidate(candidate.id, 'dismiss'), children: t('dismiss') })] }, candidate.id))) })] })] })), _jsx(SectionTitle, { icon: "\u2705", children: t('active') }), payload.active.length === 0 ? _jsx(EmptyState, { icon: "\uD83D\uDCED", message: t('empty') }) : (_jsx(Card, { padding: 0, children: _jsxs("table", { style: tableStyles.table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableStyles.th, children: "Id" }), _jsx("th", { style: tableStyles.th, children: t('memory') }), _jsx("th", { style: tableStyles.th, children: t('kind') }), _jsx("th", { style: tableStyles.th, children: t('status') }), _jsx("th", { style: tableStyles.th, children: t('from') }), _jsx("th", { style: tableStyles.th })] }) }), _jsx("tbody", { children: payload.active.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: tableStyles.td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: tableStyles.td, children: memory.text }), _jsx("td", { style: tableStyles.td, children: _jsx(Badge, { children: memory.kind }) }), _jsx("td", { style: tableStyles.td, children: checkLabel(memory, t) }), _jsx("td", { style: { ...tableStyles.td, fontSize: 11, opacity: 0.6 }, children: provenance(memory) }), _jsx("td", { style: tableStyles.td, children: _jsx(Button, { variant: "danger", size: "sm", onClick: () => setForgetTarget({ id: memory.id, scope: memory.scope, reason: 'superseded from panel' }), children: "\uD83D\uDDD1" }) })] }, memoryKey(memory)))) })] }) })), payload.superseded.length > 0 && (_jsxs(_Fragment, { children: [_jsxs(SectionTitle, { icon: "\uD83D\uDCE6", children: [t('superseded'), " (", payload.superseded.length, ")"] }), _jsx(Card, { padding: 0, children: _jsxs("table", { style: tableStyles.table, children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { style: tableStyles.th, children: "Id" }), _jsx("th", { style: tableStyles.th, children: t('memory') }), _jsx("th", { style: tableStyles.th, children: t('kind') }), _jsx("th", { style: tableStyles.th, children: t('status') })] }) }), _jsx("tbody", { children: payload.superseded.map((memory) => (_jsxs("tr", { children: [_jsx("td", { style: tableStyles.td, children: _jsx("code", { style: { fontSize: 11 }, children: memory.id }) }), _jsx("td", { style: tableStyles.td, children: memory.text }), _jsx("td", { style: tableStyles.td, children: _jsx(Badge, { children: memory.kind }) }), _jsx("td", { style: tableStyles.td, children: checkLabel(memory, t) })] }, memoryKey(memory)))) })] }) })] })), addOpen && _jsx(AddMemoryModal, { t: t, onClose: () => setAddOpen(false), onAdded: reload }), forgetTarget !== null && _jsx(ConfirmDialog, { title: t('forget'), message: t('confirmForget'), confirmLabel: t('forget'), danger: true, onConfirm: handleForget, onClose: () => setForgetTarget(null) })] }));
99
114
  }
100
115
  export function ContextMemoryPanel({ t }) {
101
116
  return _jsx(ToastProvider, { children: _jsx(ContextMemoryPanelInner, { t: t }) });
package/lib/guard.d.ts CHANGED
@@ -9,58 +9,120 @@
9
9
  *
10
10
  * Recognition is narrow on purpose. Only statement shapes whose checkable predicate is
11
11
  * unambiguous get a guard; everything else stays uncheckable and says so. Emitting an
12
- * approximate command would produce a check that exits 0 while verifying nothing, and
13
- * every later "verified" would be a lie.
12
+ * approximate check would produce one that passes while verifying nothing, and every
13
+ * later "verified" would be a lie.
14
14
  *
15
15
  * There is deliberately **no** shape that runs a command taken from memory text. A
16
- * memory can be written by the agent itself, so treating its content as an executable
17
- * command is an injection path — the templates below only ever interpolate quoted file
18
- * paths and literal search strings.
16
+ * memory can be written by the agent itself, so treating its content as executable is
17
+ * an injection path. That is why the checks below are evaluated in-process against the
18
+ * filesystem rather than handed to a shell: the statement supplies operands (a path, a
19
+ * search string) and never a program, an argument list, or shell syntax.
20
+ *
21
+ * **Why not a shell command.** This module used to compile each shape into a shell
22
+ * command string and run it with `spawnSync(..., { shell: true })`, branching on
23
+ * `process.platform`. That design made the central question — did the check run at
24
+ * all? — undecidable on Windows. A guard whose program is missing and a guard whose
25
+ * predicate is simply false both exit 1 through cmd.exe, and cmd.exe's own diagnostic
26
+ * is a localized resource string: on a zh-CN host it arrives as GBK bytes reading
27
+ * 不是内部或外部命令…, which decodes to mojibake under utf8, so neither an English nor
28
+ * a Chinese pattern could reliably tell the two apart. Any classifier built on "what
29
+ * did the shell say" is a classifier that answers differently per host language.
30
+ *
31
+ * The predicates are all filesystem facts. Four of them are about a file's *text*
32
+ * (`exists` / `absent` / `contains` / `lacks`); four are about a file's *structure*
33
+ * (`depends` / `script` / `option` / `lineLimit`), answered by `JSON.parse` and a line
34
+ * count. Answered directly, they need no shell, no locale, no platform branch, and no
35
+ * quoting layer — and the distinction this module exists to protect becomes a fact about
36
+ * a return value instead of a guess about text.
37
+ *
38
+ * The structured predicates exist because the text ones cannot answer structural
39
+ * questions honestly: `contains "tsx" package.json` passes when `tsx` appears in a
40
+ * description or a script string, so it would report `passed` while verifying nothing.
41
+ * Parsing is still in-process, and the memory still supplies only operands.
19
42
  *
20
43
  * @module context-memory/guard
21
44
  */
22
45
  import type { Guard, GuardOutcome } from './types.js';
23
46
  /**
24
- * Compile a memory statement into an executable check.
47
+ * Every guard kind this version can evaluate.
48
+ *
49
+ * This list is the single definition. `memory.ts` drops a guard whose kind is not in it
50
+ * (a log written by a newer version), and the exhaustiveness assertion below turns
51
+ * "added a kind to the union but forgot to teach the evaluator" into a compile error
52
+ * rather than a runtime fallthrough into whichever branch happens to come last.
53
+ */
54
+ export declare const GUARD_KINDS: readonly ["exists", "absent", "contains", "lacks", "depends", "script", "option", "lineLimit"];
55
+ /**
56
+ * Compile a memory statement into a check.
57
+ *
58
+ * The returned predicate names operands, never a program: `{ kind: 'exists', path }`
59
+ * says what must be true, and `runGuard` decides it with `fs`. Nothing from the
60
+ * statement text is ever executed, so a memory cannot smuggle in an injection.
61
+ *
62
+ * Operands are stored as written. Resolution against the project directory happens at
63
+ * run time, so a memory stays meaningful if the project is moved or the check is
64
+ * evaluated from a different working directory.
65
+ */
66
+ export declare function compileGuard(text: string): Guard | undefined;
67
+ /**
68
+ * A human-readable rendering of a check, for the CLI and the log.
25
69
  *
26
- * Commands are emitted for the platform in use. `process.platform` rather than a
27
- * hardcoded POSIX spelling: `test -e` on Windows would leave every compiled memory
28
- * permanently unrunnable, so verification would report on nothing while looking busy.
70
+ * Deliberately not a shell command: printing one would invite the reader to run it,
71
+ * which is the interface this module just removed.
29
72
  */
30
- export declare function compileGuard(text: string, platform?: NodeJS.Platform): Guard | undefined;
73
+ export declare function describeGuard(guard: Guard): string;
31
74
  /**
32
- * Quote a string so a POSIX shell treats it as one literal argument.
75
+ * Why a check contradicted a memory, in one line.
33
76
  *
34
- * Single quotes make every character literal; the only character that cannot appear
35
- * inside them is the quote itself, escaped by closing, escaping, and reopening.
77
+ * `runGuard` reports a reason when the check could not run, but a check that ran and came
78
+ * back false has nothing to report beyond its own verdict — `exists` and `absent` return
79
+ * an empty `output`. Answering "contradicted" with no detail leaves the reader unable to
80
+ * tell which path or which string was actually tested, so this falls back to the check's
81
+ * own description: it names the operand the memory no longer matches, which is the fact
82
+ * that made the memory wrong.
36
83
  */
37
- export declare function shellQuote(value: string): string;
84
+ export declare function describeContradiction(check: Guard | undefined, output: string | undefined): string;
38
85
  /**
39
- * Run one guard to completion.
86
+ * Resolve a statement's operand against the project directory.
40
87
  *
41
- * `spawnSync` with a shell so compound commands work as written. No timeout: a guard
42
- * that hangs is a hang, and killing it after N seconds would turn "slow" into an
43
- * outcome that varies with machine load.
88
+ * Absolute operands are kept as written. Anything else is relative to the project the
89
+ * memory was recorded in, because that is the directory the statement was talking
90
+ * about — resolving against `process.cwd()` instead would make the same memory mean
91
+ * different things depending on where the command happened to be invoked.
92
+ */
93
+ export declare function resolveOperand(path: string, cwd: string): string;
94
+ /**
95
+ * Evaluate one check and say what happened.
96
+ *
97
+ * The three-way split is the whole point. `broken` covers "the check could not be
98
+ * carried out" — an operand that resolves to nothing, a path that is a directory where
99
+ * a file was named, a file too large to scan, a manifest that will not parse — and is
100
+ * never evidence about the memory. `violated` is reserved for a check that ran and came
101
+ * back false.
102
+ *
103
+ * No exit code is invented here. There is no child process, so there is no status to
104
+ * report; `exitCode` is null and `output` carries the reason. The one place a caller
105
+ * needs a process-style signal — `ContextMemory.exitCode` — derives it from the summary,
106
+ * counting only `violated`.
44
107
  */
45
108
  export declare function runGuard(id: string, guard: Guard, cwd: string, now?: Date): {
46
109
  id: string;
47
110
  outcome: GuardOutcome;
48
- exitCode: null;
49
- output: string;
50
- at: string;
51
- } | {
52
- id: string;
53
- outcome: GuardOutcome;
54
- exitCode: number;
111
+ exitCode: number | null;
55
112
  output: string;
56
113
  at: string;
57
114
  };
58
115
  /**
59
116
  * Classify an exit code into an outcome.
60
117
  *
61
- * Two conventions mean "the command does not exist": POSIX shells use 127, cmd.exe
62
- * uses 9009. Both are recognised, along with the text shells print instead of a
63
- * distinctive code when the failure happens inside the shell. Without this, a
64
- * misspelled path would look like a memory being wrong about the world.
118
+ * Kept for callers holding a status they did not produce themselves — the CLI reports
119
+ * on log entries written by older versions of this plugin, which recorded a child
120
+ * process's status. 127 is the POSIX convention for "command not found" and 9009 is
121
+ * cmd.exe's, and both mean the check never ran.
122
+ *
123
+ * The textual fallback is gone along with the shell. It could only ever have worked on
124
+ * an English host, and a classifier that reads differently depending on the system
125
+ * language is not a classifier. `runGuard` no longer spawns anything, so no new entry
126
+ * can carry a status that needs interpreting here.
65
127
  */
66
- export declare function classify(code: number, output?: string): GuardOutcome;
128
+ export declare function classify(code: number): GuardOutcome;