instar 1.3.127 → 1.3.129
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/skills/autonomous/hooks/autonomous-stop-hook.sh +36 -0
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +29 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +64 -5
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PreferencesManager.d.ts +121 -0
- package/dist/core/PreferencesManager.d.ts.map +1 -0
- package/dist/core/PreferencesManager.js +223 -0
- package/dist/core/PreferencesManager.js.map +1 -0
- package/dist/core/installCodexHooks.d.ts +7 -0
- package/dist/core/installCodexHooks.d.ts.map +1 -1
- package/dist/core/installCodexHooks.js +30 -2
- package/dist/core/installCodexHooks.js.map +1 -1
- package/dist/core/types.d.ts +62 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +6 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +12 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +36 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +62 -62
- package/src/scaffold/templates.ts +6 -0
- package/upgrades/1.3.128.md +84 -0
- package/upgrades/1.3.129.md +55 -0
- package/upgrades/side-effects/codex-autonomous-loop-driver.md +91 -0
- package/upgrades/side-effects/correction-preference-learning-sentinel-slice1a.md +132 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreferencesManager — the structured on-disk substrate for auto-learned user
|
|
3
|
+
* preferences (Correction & Preference Learning Sentinel, Slice 1a).
|
|
4
|
+
*
|
|
5
|
+
* Modeled directly on the ORG-INTENT precedent (`OrgIntentManager` +
|
|
6
|
+
* `formatOrgIntentForSessionStart` + `GET /intent/org/session-context` +
|
|
7
|
+
* the session-start hook fetch). Where ORG-INTENT is a human-authored markdown
|
|
8
|
+
* contract, this file is a machine-written JSON store of preferences the
|
|
9
|
+
* correction loop has learned about THIS user.
|
|
10
|
+
*
|
|
11
|
+
* Design constraints (spec §3.6 / §10 Slice 1a):
|
|
12
|
+
* - `recordPreference()` is the ONLY writer to `.instar/preferences.json`.
|
|
13
|
+
* - Writes are atomic (temp file + rename) and schema-versioned.
|
|
14
|
+
* - Upsert keyed on `dedupeKey` — a recurring learning collapses to ONE entry
|
|
15
|
+
* (its `dedupeCount` increments, `recordedAt` refreshes, `confidence` takes
|
|
16
|
+
* the max of old/new), never a growing pile of duplicates.
|
|
17
|
+
* - Absent file ≡ no preferences (never throws; reads return an empty store).
|
|
18
|
+
* - The session-start block is bounded by `maxInjectedPreferencesBytes` and
|
|
19
|
+
* priority-ordered by recency × confidence × dedupeCount.
|
|
20
|
+
*
|
|
21
|
+
* SIGNAL-ONLY: this surface NEVER blocks or rewrites an outbound message. It
|
|
22
|
+
* only stores preferences, serves them, and the session-start hook injects them
|
|
23
|
+
* so the agent always SEES them. There is no enforcement gate. (The blocking
|
|
24
|
+
* idea was explicitly rejected by the user — see spec frontmatter.)
|
|
25
|
+
*/
|
|
26
|
+
/** Current on-disk schema version. Bump only with a paired migration. */
|
|
27
|
+
export declare const PREFERENCES_SCHEMA_VERSION = 1;
|
|
28
|
+
/** Provenance of a preference entry. Slice 1a only ever writes 'correction-loop'. */
|
|
29
|
+
export type PreferenceProvenance = 'correction-loop';
|
|
30
|
+
/** A single learned preference, as persisted in `.instar/preferences.json`. */
|
|
31
|
+
export interface PreferenceEntry {
|
|
32
|
+
/** The distilled, scrubbed lesson — e.g. "Lead with the one action, no preamble." */
|
|
33
|
+
learning: string;
|
|
34
|
+
/** Where this came from. Slice 1a: always 'correction-loop'. */
|
|
35
|
+
provenance: PreferenceProvenance;
|
|
36
|
+
/** Stable key the loop upserts on (kind:normalizedLearningHash). */
|
|
37
|
+
dedupeKey: string;
|
|
38
|
+
/** ISO timestamp of the most recent observation of this preference. */
|
|
39
|
+
recordedAt: string;
|
|
40
|
+
/** Loop-assigned confidence in [0,1]; advisory ordering weight. */
|
|
41
|
+
confidence: number;
|
|
42
|
+
/** How many distinct observations have collapsed into this entry (≥1). */
|
|
43
|
+
dedupeCount: number;
|
|
44
|
+
}
|
|
45
|
+
/** The full on-disk store shape. */
|
|
46
|
+
export interface PreferencesStore {
|
|
47
|
+
schemaVersion: number;
|
|
48
|
+
preferences: PreferenceEntry[];
|
|
49
|
+
}
|
|
50
|
+
/** Input payload for `recordPreference()`. */
|
|
51
|
+
export interface RecordPreferencePayload {
|
|
52
|
+
learning: string;
|
|
53
|
+
dedupeKey: string;
|
|
54
|
+
/** Defaults to 0.5 when omitted; clamped to [0,1]. */
|
|
55
|
+
confidence?: number;
|
|
56
|
+
/** Defaults to 'correction-loop'. */
|
|
57
|
+
provenance?: PreferenceProvenance;
|
|
58
|
+
/** Defaults to now (ISO). Injectable for deterministic tests. */
|
|
59
|
+
recordedAt?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Options for `recordPreference()`. */
|
|
62
|
+
export interface RecordPreferenceOptions {
|
|
63
|
+
/**
|
|
64
|
+
* Priority ordering for the injected block. Mirrors the spec's
|
|
65
|
+
* `preferencesInjectionPriority` config string. Slice 1a supports the single
|
|
66
|
+
* documented form; unknown strings fall back to it.
|
|
67
|
+
*/
|
|
68
|
+
injectionPriority?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Render the active preferences into a session-start text block, bounded by
|
|
72
|
+
* `maxBytes` and priority-ordered. Returns the block string (may be empty).
|
|
73
|
+
*
|
|
74
|
+
* Mirrors `formatOrgIntentForSessionStart` — deterministic, no LLM, safe to
|
|
75
|
+
* inject directly. Each preference is wrapped at the section level inside an
|
|
76
|
+
* `<auto-learned-preference src='correction-loop'>` envelope so downstream
|
|
77
|
+
* prompt assemblers structurally cannot mistake a learned preference for an
|
|
78
|
+
* authoritative instruction (spec §3.6).
|
|
79
|
+
*/
|
|
80
|
+
export declare function formatPreferencesForSessionStart(store: PreferencesStore, maxBytes?: number): string;
|
|
81
|
+
export declare class PreferencesManager {
|
|
82
|
+
private stateDir;
|
|
83
|
+
private preferencesPath;
|
|
84
|
+
constructor(stateDir: string);
|
|
85
|
+
/** Absolute path to the backing file (for tests / observability). */
|
|
86
|
+
getPath(): string;
|
|
87
|
+
/** Whether the backing file exists on disk. */
|
|
88
|
+
exists(): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Read the store. Absent file ≡ empty store (never throws). A malformed or
|
|
91
|
+
* unexpected-shape file is also treated as empty — the loop is signal-only,
|
|
92
|
+
* so a corrupt read should never crash a session start.
|
|
93
|
+
*/
|
|
94
|
+
read(): PreferencesStore;
|
|
95
|
+
/**
|
|
96
|
+
* recordPreference — the ONLY writer to `.instar/preferences.json`.
|
|
97
|
+
*
|
|
98
|
+
* Upserts the payload keyed on `dedupeKey`:
|
|
99
|
+
* - New key → appended as a fresh entry with `dedupeCount: 1`.
|
|
100
|
+
* - Existing key → `learning` refreshed, `recordedAt` advanced,
|
|
101
|
+
* `confidence` raised to max(old, new), `dedupeCount` incremented.
|
|
102
|
+
*
|
|
103
|
+
* Atomic: writes a temp file then renames over the target so a concurrent
|
|
104
|
+
* reader never observes a partial file. Returns the entry as persisted.
|
|
105
|
+
*/
|
|
106
|
+
recordPreference(payload: RecordPreferencePayload, _opts?: RecordPreferenceOptions): PreferenceEntry;
|
|
107
|
+
/**
|
|
108
|
+
* Build the session-context payload served by `GET /preferences/session-context`.
|
|
109
|
+
* Serves ONLY the `learning` text + metadata via the formatted block — never
|
|
110
|
+
* any raw extras. `present` is true iff at least one preference would be
|
|
111
|
+
* injected into the (bounded) block.
|
|
112
|
+
*/
|
|
113
|
+
sessionContext(maxBytes?: number): {
|
|
114
|
+
present: boolean;
|
|
115
|
+
block: string;
|
|
116
|
+
count: number;
|
|
117
|
+
};
|
|
118
|
+
/** Atomic write: temp file + fsync + rename. */
|
|
119
|
+
private writeAtomic;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=PreferencesManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PreferencesManager.d.ts","sourceRoot":"","sources":["../../src/core/PreferencesManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAOH,yEAAyE;AACzE,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAE5C,qFAAqF;AACrF,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAErD,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,UAAU,EAAE,oBAAoB,CAAC;IACjC,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,oCAAoC;AACpC,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,8CAA8C;AAC9C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wCAAwC;AACxC,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AA6BD;;;;;;;;;GASG;AACH,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,gBAAgB,EACvB,QAAQ,SAAO,GACd,MAAM,CAmBR;AAID,qBAAa,kBAAkB;IAGjB,OAAO,CAAC,QAAQ;IAF5B,OAAO,CAAC,eAAe,CAAS;gBAEZ,QAAQ,EAAE,MAAM;IAIpC,qEAAqE;IACrE,OAAO,IAAI,MAAM;IAIjB,+CAA+C;IAC/C,MAAM,IAAI,OAAO;IAIjB;;;;OAIG;IACH,IAAI,IAAI,gBAAgB;IAqCxB;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,EAAE,KAAK,GAAE,uBAA4B,GAAG,eAAe;IAuCxG;;;;;OAKG;IACH,cAAc,CAAC,QAAQ,SAAO,GAAG;QAC/B,OAAO,EAAE,OAAO,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;KACf;IAUD,gDAAgD;IAChD,OAAO,CAAC,WAAW;CAcpB"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreferencesManager — the structured on-disk substrate for auto-learned user
|
|
3
|
+
* preferences (Correction & Preference Learning Sentinel, Slice 1a).
|
|
4
|
+
*
|
|
5
|
+
* Modeled directly on the ORG-INTENT precedent (`OrgIntentManager` +
|
|
6
|
+
* `formatOrgIntentForSessionStart` + `GET /intent/org/session-context` +
|
|
7
|
+
* the session-start hook fetch). Where ORG-INTENT is a human-authored markdown
|
|
8
|
+
* contract, this file is a machine-written JSON store of preferences the
|
|
9
|
+
* correction loop has learned about THIS user.
|
|
10
|
+
*
|
|
11
|
+
* Design constraints (spec §3.6 / §10 Slice 1a):
|
|
12
|
+
* - `recordPreference()` is the ONLY writer to `.instar/preferences.json`.
|
|
13
|
+
* - Writes are atomic (temp file + rename) and schema-versioned.
|
|
14
|
+
* - Upsert keyed on `dedupeKey` — a recurring learning collapses to ONE entry
|
|
15
|
+
* (its `dedupeCount` increments, `recordedAt` refreshes, `confidence` takes
|
|
16
|
+
* the max of old/new), never a growing pile of duplicates.
|
|
17
|
+
* - Absent file ≡ no preferences (never throws; reads return an empty store).
|
|
18
|
+
* - The session-start block is bounded by `maxInjectedPreferencesBytes` and
|
|
19
|
+
* priority-ordered by recency × confidence × dedupeCount.
|
|
20
|
+
*
|
|
21
|
+
* SIGNAL-ONLY: this surface NEVER blocks or rewrites an outbound message. It
|
|
22
|
+
* only stores preferences, serves them, and the session-start hook injects them
|
|
23
|
+
* so the agent always SEES them. There is no enforcement gate. (The blocking
|
|
24
|
+
* idea was explicitly rejected by the user — see spec frontmatter.)
|
|
25
|
+
*/
|
|
26
|
+
import fs from 'node:fs';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
// ── Types ────────────────────────────────────────────────────────────
|
|
29
|
+
/** Current on-disk schema version. Bump only with a paired migration. */
|
|
30
|
+
export const PREFERENCES_SCHEMA_VERSION = 1;
|
|
31
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
32
|
+
const EMPTY_STORE = { schemaVersion: PREFERENCES_SCHEMA_VERSION, preferences: [] };
|
|
33
|
+
function clampConfidence(value) {
|
|
34
|
+
if (typeof value !== 'number' || Number.isNaN(value))
|
|
35
|
+
return 0.5;
|
|
36
|
+
if (value < 0)
|
|
37
|
+
return 0;
|
|
38
|
+
if (value > 1)
|
|
39
|
+
return 1;
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Compute the priority score for ordering injected preferences. Higher first.
|
|
44
|
+
* `recency × confidence × dedupeCount` per the spec default. Recency is a
|
|
45
|
+
* monotonically-increasing epoch-ms reading of `recordedAt` (newer = larger),
|
|
46
|
+
* so a fresher, higher-confidence, more-recurrent preference sorts first.
|
|
47
|
+
*/
|
|
48
|
+
function priorityScore(entry) {
|
|
49
|
+
const recencyMs = Date.parse(entry.recordedAt);
|
|
50
|
+
const recency = Number.isNaN(recencyMs) ? 0 : recencyMs;
|
|
51
|
+
const confidence = clampConfidence(entry.confidence);
|
|
52
|
+
const dedupeCount = Math.max(1, entry.dedupeCount || 1);
|
|
53
|
+
// recencyMs dominates the magnitude, which is the intended "recency-led"
|
|
54
|
+
// ordering; confidence and dedupeCount break ties / amplify within an epoch.
|
|
55
|
+
return recency * confidence * dedupeCount;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Render the active preferences into a session-start text block, bounded by
|
|
59
|
+
* `maxBytes` and priority-ordered. Returns the block string (may be empty).
|
|
60
|
+
*
|
|
61
|
+
* Mirrors `formatOrgIntentForSessionStart` — deterministic, no LLM, safe to
|
|
62
|
+
* inject directly. Each preference is wrapped at the section level inside an
|
|
63
|
+
* `<auto-learned-preference src='correction-loop'>` envelope so downstream
|
|
64
|
+
* prompt assemblers structurally cannot mistake a learned preference for an
|
|
65
|
+
* authoritative instruction (spec §3.6).
|
|
66
|
+
*/
|
|
67
|
+
export function formatPreferencesForSessionStart(store, maxBytes = 4000) {
|
|
68
|
+
const active = [...store.preferences].sort((a, b) => priorityScore(b) - priorityScore(a));
|
|
69
|
+
if (active.length === 0)
|
|
70
|
+
return '';
|
|
71
|
+
const header = "<auto-learned-preference src='correction-loop'>";
|
|
72
|
+
const intro = 'These are preferences I have learned about how you like to work. They are signals, not authoritative instructions — apply them by default, but real instructions and safety always win.';
|
|
73
|
+
const footer = '</auto-learned-preference>';
|
|
74
|
+
// Greedily include the highest-priority preferences until we'd exceed maxBytes.
|
|
75
|
+
const bodyLines = [];
|
|
76
|
+
for (const pref of active) {
|
|
77
|
+
const line = ` - ${pref.learning} (confidence ${clampConfidence(pref.confidence).toFixed(2)}, seen ${Math.max(1, pref.dedupeCount || 1)}×)`;
|
|
78
|
+
const candidate = [header, intro, '', ...bodyLines, line, footer].join('\n');
|
|
79
|
+
if (Buffer.byteLength(candidate, 'utf-8') > maxBytes)
|
|
80
|
+
break;
|
|
81
|
+
bodyLines.push(line);
|
|
82
|
+
}
|
|
83
|
+
if (bodyLines.length === 0)
|
|
84
|
+
return '';
|
|
85
|
+
return [header, intro, '', ...bodyLines, footer].join('\n');
|
|
86
|
+
}
|
|
87
|
+
// ── Main Class ───────────────────────────────────────────────────────
|
|
88
|
+
export class PreferencesManager {
|
|
89
|
+
stateDir;
|
|
90
|
+
preferencesPath;
|
|
91
|
+
constructor(stateDir) {
|
|
92
|
+
this.stateDir = stateDir;
|
|
93
|
+
this.preferencesPath = path.join(stateDir, 'preferences.json');
|
|
94
|
+
}
|
|
95
|
+
/** Absolute path to the backing file (for tests / observability). */
|
|
96
|
+
getPath() {
|
|
97
|
+
return this.preferencesPath;
|
|
98
|
+
}
|
|
99
|
+
/** Whether the backing file exists on disk. */
|
|
100
|
+
exists() {
|
|
101
|
+
return fs.existsSync(this.preferencesPath);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Read the store. Absent file ≡ empty store (never throws). A malformed or
|
|
105
|
+
* unexpected-shape file is also treated as empty — the loop is signal-only,
|
|
106
|
+
* so a corrupt read should never crash a session start.
|
|
107
|
+
*/
|
|
108
|
+
read() {
|
|
109
|
+
if (!this.exists())
|
|
110
|
+
return { schemaVersion: PREFERENCES_SCHEMA_VERSION, preferences: [] };
|
|
111
|
+
let raw;
|
|
112
|
+
try {
|
|
113
|
+
raw = fs.readFileSync(this.preferencesPath, 'utf-8');
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// @silent-fallback-ok — unreadable preferences file ≡ no preferences
|
|
117
|
+
return { schemaVersion: PREFERENCES_SCHEMA_VERSION, preferences: [] };
|
|
118
|
+
}
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(raw);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// @silent-fallback-ok — malformed preferences file ≡ no preferences
|
|
125
|
+
return { schemaVersion: PREFERENCES_SCHEMA_VERSION, preferences: [] };
|
|
126
|
+
}
|
|
127
|
+
if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed.preferences)) {
|
|
128
|
+
return { schemaVersion: PREFERENCES_SCHEMA_VERSION, preferences: [] };
|
|
129
|
+
}
|
|
130
|
+
const store = parsed;
|
|
131
|
+
const preferences = store.preferences.filter((p) => !!p && typeof p.learning === 'string' && typeof p.dedupeKey === 'string').map((p) => ({
|
|
132
|
+
learning: p.learning,
|
|
133
|
+
provenance: (p.provenance ?? 'correction-loop'),
|
|
134
|
+
dedupeKey: p.dedupeKey,
|
|
135
|
+
recordedAt: typeof p.recordedAt === 'string' ? p.recordedAt : new Date(0).toISOString(),
|
|
136
|
+
confidence: clampConfidence(p.confidence),
|
|
137
|
+
dedupeCount: Math.max(1, Math.floor(p.dedupeCount) || 1),
|
|
138
|
+
}));
|
|
139
|
+
return {
|
|
140
|
+
schemaVersion: typeof store.schemaVersion === 'number' ? store.schemaVersion : PREFERENCES_SCHEMA_VERSION,
|
|
141
|
+
preferences,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* recordPreference — the ONLY writer to `.instar/preferences.json`.
|
|
146
|
+
*
|
|
147
|
+
* Upserts the payload keyed on `dedupeKey`:
|
|
148
|
+
* - New key → appended as a fresh entry with `dedupeCount: 1`.
|
|
149
|
+
* - Existing key → `learning` refreshed, `recordedAt` advanced,
|
|
150
|
+
* `confidence` raised to max(old, new), `dedupeCount` incremented.
|
|
151
|
+
*
|
|
152
|
+
* Atomic: writes a temp file then renames over the target so a concurrent
|
|
153
|
+
* reader never observes a partial file. Returns the entry as persisted.
|
|
154
|
+
*/
|
|
155
|
+
recordPreference(payload, _opts = {}) {
|
|
156
|
+
if (!payload || typeof payload.learning !== 'string' || !payload.learning.trim()) {
|
|
157
|
+
throw new Error('recordPreference: `learning` is required and must be a non-empty string');
|
|
158
|
+
}
|
|
159
|
+
if (typeof payload.dedupeKey !== 'string' || !payload.dedupeKey.trim()) {
|
|
160
|
+
throw new Error('recordPreference: `dedupeKey` is required and must be a non-empty string');
|
|
161
|
+
}
|
|
162
|
+
const store = this.read();
|
|
163
|
+
const now = payload.recordedAt ?? new Date().toISOString();
|
|
164
|
+
const confidence = clampConfidence(payload.confidence);
|
|
165
|
+
const provenance = payload.provenance ?? 'correction-loop';
|
|
166
|
+
const existing = store.preferences.find((p) => p.dedupeKey === payload.dedupeKey);
|
|
167
|
+
let result;
|
|
168
|
+
if (existing) {
|
|
169
|
+
existing.learning = payload.learning.trim();
|
|
170
|
+
existing.recordedAt = now;
|
|
171
|
+
existing.confidence = Math.max(clampConfidence(existing.confidence), confidence);
|
|
172
|
+
existing.dedupeCount = Math.max(1, existing.dedupeCount || 1) + 1;
|
|
173
|
+
existing.provenance = provenance;
|
|
174
|
+
result = existing;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
result = {
|
|
178
|
+
learning: payload.learning.trim(),
|
|
179
|
+
provenance,
|
|
180
|
+
dedupeKey: payload.dedupeKey,
|
|
181
|
+
recordedAt: now,
|
|
182
|
+
confidence,
|
|
183
|
+
dedupeCount: 1,
|
|
184
|
+
};
|
|
185
|
+
store.preferences.push(result);
|
|
186
|
+
}
|
|
187
|
+
store.schemaVersion = PREFERENCES_SCHEMA_VERSION;
|
|
188
|
+
this.writeAtomic(store);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Build the session-context payload served by `GET /preferences/session-context`.
|
|
193
|
+
* Serves ONLY the `learning` text + metadata via the formatted block — never
|
|
194
|
+
* any raw extras. `present` is true iff at least one preference would be
|
|
195
|
+
* injected into the (bounded) block.
|
|
196
|
+
*/
|
|
197
|
+
sessionContext(maxBytes = 4000) {
|
|
198
|
+
const store = this.read();
|
|
199
|
+
const block = formatPreferencesForSessionStart(store, maxBytes);
|
|
200
|
+
return {
|
|
201
|
+
present: block.length > 0,
|
|
202
|
+
block,
|
|
203
|
+
count: store.preferences.length,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/** Atomic write: temp file + fsync + rename. */
|
|
207
|
+
writeAtomic(store) {
|
|
208
|
+
const dir = path.dirname(this.preferencesPath);
|
|
209
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
210
|
+
const tmp = `${this.preferencesPath}.tmp.${process.pid}.${Date.now()}`;
|
|
211
|
+
const data = JSON.stringify(store, null, 2);
|
|
212
|
+
const fd = fs.openSync(tmp, 'w');
|
|
213
|
+
try {
|
|
214
|
+
fs.writeSync(fd, data);
|
|
215
|
+
fs.fsyncSync(fd);
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
fs.closeSync(fd);
|
|
219
|
+
}
|
|
220
|
+
fs.renameSync(tmp, this.preferencesPath);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=PreferencesManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PreferencesManager.js","sourceRoot":"","sources":["../../src/core/PreferencesManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
*/
|
|
33
33
|
/** Marker that identifies an instar-owned hook command (for merge-safe replace). */
|
|
34
34
|
export declare const INSTAR_HOOK_PATH_MARKER = ".instar/hooks/instar/";
|
|
35
|
+
/**
|
|
36
|
+
* The autonomous-loop driver hook lives in the autonomous skill, not under
|
|
37
|
+
* `.instar/hooks/instar/`, so it needs its own ownership marker — used both to build
|
|
38
|
+
* its command path and to let groupIsInstarOwned() recognize (and replace, never
|
|
39
|
+
* duplicate) the autonomous Stop group on every re-install.
|
|
40
|
+
*/
|
|
41
|
+
export declare const CODEX_AUTONOMOUS_HOOK_MARKER = ".claude/skills/autonomous/hooks/autonomous-stop-hook.sh";
|
|
35
42
|
interface CodexHookHandler {
|
|
36
43
|
type: 'command';
|
|
37
44
|
command: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installCodexHooks.d.ts","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"installCodexHooks.d.ts","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAC/D;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,4DAA4D,CAAC;AAEtG,UAAU,gBAAgB;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AACD,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,gBAAgB,EAAE,CAAC;CAC3B;AAMD,2FAA2F;AAC3F,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CA4ElC;AAWD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAwB5D"}
|
|
@@ -34,6 +34,13 @@ import fs from 'node:fs';
|
|
|
34
34
|
import path from 'node:path';
|
|
35
35
|
/** Marker that identifies an instar-owned hook command (for merge-safe replace). */
|
|
36
36
|
export const INSTAR_HOOK_PATH_MARKER = '.instar/hooks/instar/';
|
|
37
|
+
/**
|
|
38
|
+
* The autonomous-loop driver hook lives in the autonomous skill, not under
|
|
39
|
+
* `.instar/hooks/instar/`, so it needs its own ownership marker — used both to build
|
|
40
|
+
* its command path and to let groupIsInstarOwned() recognize (and replace, never
|
|
41
|
+
* duplicate) the autonomous Stop group on every re-install.
|
|
42
|
+
*/
|
|
43
|
+
export const CODEX_AUTONOMOUS_HOOK_MARKER = '.claude/skills/autonomous/hooks/autonomous-stop-hook.sh';
|
|
37
44
|
/** Build the instar-owned hook groups for each Codex event, with absolute script paths. */
|
|
38
45
|
export function buildInstarCodexHookGroups(projectDir) {
|
|
39
46
|
const node = (script) => ({
|
|
@@ -46,6 +53,15 @@ export function buildInstarCodexHookGroups(projectDir) {
|
|
|
46
53
|
command: `bash ${path.join(projectDir, INSTAR_HOOK_PATH_MARKER, script)}`,
|
|
47
54
|
timeout: 5000,
|
|
48
55
|
});
|
|
56
|
+
// The autonomous-loop driver lives in the autonomous SKILL (not .instar/hooks/instar),
|
|
57
|
+
// so it has its own path constant. `--codex` tells the shared hook it was invoked from
|
|
58
|
+
// codex (vs Claude's settings.json registration) so it can self-gate on the
|
|
59
|
+
// autonomousSessions.codexLoopDriver flag (DARK by default → exits immediately).
|
|
60
|
+
const autonomousLoopHook = () => ({
|
|
61
|
+
type: 'command',
|
|
62
|
+
command: `bash ${path.join(projectDir, CODEX_AUTONOMOUS_HOOK_MARKER)} --codex`,
|
|
63
|
+
timeout: 10000,
|
|
64
|
+
});
|
|
49
65
|
return {
|
|
50
66
|
// Pre-action gate. matcher '.*' = all tool calls (Codex treats the matcher as
|
|
51
67
|
// a regex against the tool name; a bare '*' is an invalid quantifier that
|
|
@@ -80,8 +96,18 @@ export function buildInstarCodexHookGroups(projectDir) {
|
|
|
80
96
|
// semantics as Claude, NOT a hard termination. scope-coherence defaults to
|
|
81
97
|
// `approve` and self-throttles (depth threshold + 30-min cooldown), so it
|
|
82
98
|
// can't loop an autonomous Codex run.
|
|
99
|
+
// End-of-turn chain: the standing review trio, then the autonomous-loop driver as
|
|
100
|
+
// the LAST hook IN THE SAME group (slot stop:0:4) — NOT a separate group. A separate
|
|
101
|
+
// group would emit a stop:1:0 arm slot that codex may never render/trust, which would
|
|
102
|
+
// permanently break armCodexHooks's idempotency (recurring codex-TUI arm spawns on
|
|
103
|
+
// every update, even while the driver is dark). Keeping it in group 0 means it's
|
|
104
|
+
// armed/trusted/fired alongside the trio it already runs. The driver self-gates: with
|
|
105
|
+
// codexLoopDriver disabled (default) it exits immediately, so it's a no-op until the
|
|
106
|
+
// flag is flipped on. (Within-group block precedence vs the trio is verified live
|
|
107
|
+
// before enabling — see the spec's Phase 5; worst case the session still continues
|
|
108
|
+
// via the unjustified-stop router, never strands.)
|
|
83
109
|
Stop: [
|
|
84
|
-
{ matcher: '', hooks: [node('stop-gate-router.js'), { ...node('response-review.js'), timeout: 10000 }, node('claim-intercept-response.js'), node('scope-coherence-checkpoint.js')] },
|
|
110
|
+
{ matcher: '', hooks: [node('stop-gate-router.js'), { ...node('response-review.js'), timeout: 10000 }, node('claim-intercept-response.js'), node('scope-coherence-checkpoint.js'), autonomousLoopHook()] },
|
|
85
111
|
],
|
|
86
112
|
// Identity/context injection.
|
|
87
113
|
SessionStart: [
|
|
@@ -93,7 +119,9 @@ export function buildInstarCodexHookGroups(projectDir) {
|
|
|
93
119
|
};
|
|
94
120
|
}
|
|
95
121
|
function groupIsInstarOwned(group) {
|
|
96
|
-
return (group.hooks ?? []).some((h) => typeof h.command === 'string' &&
|
|
122
|
+
return (group.hooks ?? []).some((h) => typeof h.command === 'string' &&
|
|
123
|
+
(h.command.includes(INSTAR_HOOK_PATH_MARKER) ||
|
|
124
|
+
h.command.includes(CODEX_AUTONOMOUS_HOOK_MARKER)));
|
|
97
125
|
}
|
|
98
126
|
/**
|
|
99
127
|
* Write/merge instar gate hooks into `<projectDir>/.codex/hooks.json`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installCodexHooks.js","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,oFAAoF;AACpF,MAAM,CAAC,MAAM,uBAAuB,GAAG,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"installCodexHooks.js","sourceRoot":"","sources":["../../src/core/installCodexHooks.ts"],"names":[],"mappings":"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"}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -2018,6 +2018,19 @@ export interface InstarConfig {
|
|
|
2018
2018
|
autonomousSessions?: {
|
|
2019
2019
|
/** Max concurrent autonomous jobs (default 5). New starts beyond this are refused. */
|
|
2020
2020
|
maxConcurrent?: number;
|
|
2021
|
+
/**
|
|
2022
|
+
* Codex autonomous-loop driver (codex/Claude parity). When enabled, the standing
|
|
2023
|
+
* codex Stop hook (`autonomous-stop-hook.sh --codex`, installed by installCodexHooks)
|
|
2024
|
+
* feeds the task list back so a `codex exec` session sustains a multi-turn autonomous
|
|
2025
|
+
* run instead of exiting after one turn — the codex analog of Claude's Stop-hook loop.
|
|
2026
|
+
* Ships DARK (default `enabled: false`): while off, the standing hook exits immediately
|
|
2027
|
+
* (no behavior change for any codex session), so this is a pure dark-launch flag.
|
|
2028
|
+
* Rollback = set `enabled: false` (instant, no redeploy). Claude autonomous mode is
|
|
2029
|
+
* unaffected either way (its loop hook is registered separately into settings.json).
|
|
2030
|
+
*/
|
|
2031
|
+
codexLoopDriver?: {
|
|
2032
|
+
enabled?: boolean;
|
|
2033
|
+
};
|
|
2021
2034
|
};
|
|
2022
2035
|
/** Notification preferences for autonomy events */
|
|
2023
2036
|
notifications?: NotificationPreferences;
|
|
@@ -2843,6 +2856,55 @@ export interface MonitoringConfig {
|
|
|
2843
2856
|
ciMaxRunsPerTick?: number;
|
|
2844
2857
|
};
|
|
2845
2858
|
};
|
|
2859
|
+
/**
|
|
2860
|
+
* Correction & Preference Learning Sentinel
|
|
2861
|
+
* (docs/specs/CORRECTION-PREFERENCE-LEARNING-SENTINEL-SPEC.md). Ships OFF.
|
|
2862
|
+
* Slice 1a wires only the preferences read-surface: when `enabled`, the
|
|
2863
|
+
* `GET /preferences/session-context` route serves the learned-preference
|
|
2864
|
+
* block (else 503) and the session-start hook injects it. SIGNAL-ONLY — it
|
|
2865
|
+
* never blocks or rewrites an outbound message. Slice 1b adds the capture →
|
|
2866
|
+
* distill → ledger → recurrence-gate loop that writes via `recordPreference()`.
|
|
2867
|
+
*/
|
|
2868
|
+
correctionLearning?: {
|
|
2869
|
+
/** Master kill switch (default: false). Gates the loop AND the read route. */
|
|
2870
|
+
enabled: boolean;
|
|
2871
|
+
/** Min total occurrences before a learning crosses the recurrence gate (Slice 1b). */
|
|
2872
|
+
minSupport?: number;
|
|
2873
|
+
/** Distinct calendar days required for an infra-gap learning (Slice 1b, restart-proof). */
|
|
2874
|
+
minDistinctDaysInfraGap?: number;
|
|
2875
|
+
/** Distinct calendar days required for an explicit-preference learning (Slice 1b). */
|
|
2876
|
+
minDistinctDaysPreference?: number;
|
|
2877
|
+
/** Distinct topics required for the preference path's second prong (Slice 1b). */
|
|
2878
|
+
minDistinctTopicsPreference?: number;
|
|
2879
|
+
/** Auto-submit infra-gap learnings to /feedback (Slice 1b/3). Default false. */
|
|
2880
|
+
autoFeedback?: boolean;
|
|
2881
|
+
/** Post a periodic Telegram digest of learned preferences (Slice 2). Default false. */
|
|
2882
|
+
telegramDigest?: boolean;
|
|
2883
|
+
/** Drift canary that samples un-classified messages through the LLM (Slice 1b). Default false. */
|
|
2884
|
+
driftCanary?: boolean;
|
|
2885
|
+
/** Per-sentinel LLM daily spend cap in cents (own LlmQueue instance, Slice 1b). */
|
|
2886
|
+
llmDailyCents?: number;
|
|
2887
|
+
/** Max concurrent LLM distillation calls (Slice 1b). */
|
|
2888
|
+
llmMaxConcurrent?: number;
|
|
2889
|
+
/** Per-topic look-back ring depth captured for distillation (Slice 1b). */
|
|
2890
|
+
captureContextTurns?: number;
|
|
2891
|
+
/** Max distinct topics held in the capture map before LRU eviction (Slice 1b). */
|
|
2892
|
+
captureTopicMapMax?: number;
|
|
2893
|
+
/** Idle TTL (minutes) before a topic's capture ring is evicted (Slice 1b). */
|
|
2894
|
+
captureTopicTtlMinutes?: number;
|
|
2895
|
+
/** Per-topic distillation rate ceiling per minute (Slice 1b). */
|
|
2896
|
+
distillPerTopicRatePerMinute?: number;
|
|
2897
|
+
/** Verify-window days for the infra-gap closed loop (Slice 1b/2). */
|
|
2898
|
+
verifyWindowDaysInfraGap?: number;
|
|
2899
|
+
/** Verify-window days for the preference closed loop (Slice 1b). */
|
|
2900
|
+
verifyWindowDaysPreference?: number;
|
|
2901
|
+
/** Byte cap on the injected session-start preferences block (Slice 1a). */
|
|
2902
|
+
maxInjectedPreferencesBytes?: number;
|
|
2903
|
+
/** Priority ordering string for the injected block (Slice 1a). */
|
|
2904
|
+
preferencesInjectionPriority?: string;
|
|
2905
|
+
/** Max times a closed-loop verification may reopen the same dedupeKey (Slice 1b). */
|
|
2906
|
+
maxReopens?: number;
|
|
2907
|
+
};
|
|
2846
2908
|
/**
|
|
2847
2909
|
* ReleaseReadinessSentinel (docs/specs/RELEASE-READINESS-VISIBILITY-SPEC.md §4.2)
|
|
2848
2910
|
* — Layer B. A repo-gated dev-environment watchdog: evaluates the canonical
|