instar 1.3.533 → 1.3.535

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.
Files changed (56) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +98 -2
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/CredentialWriteFunnel.d.ts +16 -0
  5. package/dist/core/CredentialWriteFunnel.d.ts.map +1 -1
  6. package/dist/core/CredentialWriteFunnel.js +19 -0
  7. package/dist/core/CredentialWriteFunnel.js.map +1 -1
  8. package/dist/core/EscalationHintStore.d.ts +60 -0
  9. package/dist/core/EscalationHintStore.d.ts.map +1 -0
  10. package/dist/core/EscalationHintStore.js +163 -0
  11. package/dist/core/EscalationHintStore.js.map +1 -0
  12. package/dist/core/ModelTierEscalation.d.ts +10 -0
  13. package/dist/core/ModelTierEscalation.d.ts.map +1 -1
  14. package/dist/core/ModelTierEscalation.js +4 -0
  15. package/dist/core/ModelTierEscalation.js.map +1 -1
  16. package/dist/core/OAuthRefresher.d.ts +18 -1
  17. package/dist/core/OAuthRefresher.d.ts.map +1 -1
  18. package/dist/core/OAuthRefresher.js +23 -1
  19. package/dist/core/OAuthRefresher.js.map +1 -1
  20. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  21. package/dist/core/PostUpdateMigrator.js +15 -0
  22. package/dist/core/PostUpdateMigrator.js.map +1 -1
  23. package/dist/core/QuotaPoller.d.ts.map +1 -1
  24. package/dist/core/QuotaPoller.js +7 -0
  25. package/dist/core/QuotaPoller.js.map +1 -1
  26. package/dist/core/TopicProfileTransferCarrier.d.ts +27 -0
  27. package/dist/core/TopicProfileTransferCarrier.d.ts.map +1 -1
  28. package/dist/core/TopicProfileTransferCarrier.js +24 -1
  29. package/dist/core/TopicProfileTransferCarrier.js.map +1 -1
  30. package/dist/monitoring/AccountSwitcher.d.ts.map +1 -1
  31. package/dist/monitoring/AccountSwitcher.js +13 -2
  32. package/dist/monitoring/AccountSwitcher.js.map +1 -1
  33. package/dist/monitoring/CredentialProvider.d.ts +20 -0
  34. package/dist/monitoring/CredentialProvider.d.ts.map +1 -1
  35. package/dist/monitoring/CredentialProvider.js +26 -0
  36. package/dist/monitoring/CredentialProvider.js.map +1 -1
  37. package/dist/scaffold/templates.d.ts.map +1 -1
  38. package/dist/scaffold/templates.js +1 -0
  39. package/dist/scaffold/templates.js.map +1 -1
  40. package/dist/server/AgentServer.d.ts +10 -0
  41. package/dist/server/AgentServer.d.ts.map +1 -1
  42. package/dist/server/AgentServer.js +26 -0
  43. package/dist/server/AgentServer.js.map +1 -1
  44. package/dist/server/routes.d.ts +6 -0
  45. package/dist/server/routes.d.ts.map +1 -1
  46. package/dist/server/routes.js +47 -1
  47. package/dist/server/routes.js.map +1 -1
  48. package/package.json +4 -2
  49. package/scripts/lint-no-direct-destructive.js +4 -0
  50. package/scripts/lint-no-unfunneled-credential-write.js +155 -0
  51. package/src/data/builtin-manifest.json +64 -64
  52. package/src/scaffold/templates.ts +1 -0
  53. package/upgrades/1.3.535.md +66 -0
  54. package/upgrades/side-effects/live-credential-repointing-increment-a-funnel-routing.md +86 -0
  55. package/upgrades/side-effects/ws53-escalation-rides-topic.md +52 -0
  56. package/upgrades/1.3.533.md +0 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.533",
3
+ "version": "1.3.535",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "test:contract": "node scripts/run-contract-tests.js",
29
29
  "test:contract:raw": "vitest run --config vitest.contract.config.ts",
30
30
  "test:all": "vitest run && vitest run --config vitest.integration.config.ts && vitest run --config vitest.e2e.config.ts",
31
- "lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-state-registry.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/check-codex-rule1-drift.js",
31
+ "lint": "tsc --noEmit && node scripts/lint-no-direct-destructive.js && node scripts/lint-no-direct-llm-http.js && node scripts/lint-no-direct-url-log.js && node scripts/lint-no-unfunneled-topic-creation.js && node scripts/lint-no-unfunneled-headless-launch.js && node scripts/lint-no-unfunneled-credential-write.js && node scripts/lint-state-registry.js && node scripts/lint-cas-emit-placement.js && node scripts/lint-journal-actuation-ban.js && node scripts/lint-no-blocking-process-scans.js && node scripts/lint-dev-agent-dark-gate.js && node scripts/lint-guard-manifest.js && node scripts/lint-llm-attribution.js && node scripts/lint-no-mainthread-cartographer-walk.js && node scripts/check-codex-rule1-drift.js",
32
32
  "lint:destructive": "node scripts/lint-no-direct-destructive.js",
33
33
  "lint:dev-agent-dark-gate": "node scripts/lint-dev-agent-dark-gate.js",
34
34
  "lint:guard-manifest": "node scripts/lint-guard-manifest.js",
@@ -38,6 +38,8 @@
38
38
  "lint:llm-attribution": "node scripts/lint-llm-attribution.js",
39
39
  "lint:llm-attribution:staged": "node scripts/lint-llm-attribution.js --staged",
40
40
  "lint:llm-http:staged": "node scripts/lint-no-direct-llm-http.js --staged",
41
+ "lint:credential-write": "node scripts/lint-no-unfunneled-credential-write.js",
42
+ "lint:credential-write:staged": "node scripts/lint-no-unfunneled-credential-write.js --staged",
41
43
  "clean": "rm -rf dist",
42
44
  "check:upgrade-guide": "node scripts/check-upgrade-guide.js",
43
45
  "check:pre-push-gate": "node scripts/pre-push-gate.js",
@@ -77,6 +77,10 @@ const ALLOWLIST = new Set([
77
77
  // Same bootstrap-escape pattern (june15-headless-spawn-reroute funnel
78
78
  // lint) — read-only `git diff --cached --name-only` only.
79
79
  'scripts/lint-no-unfunneled-headless-launch.js',
80
+ // Same bootstrap-escape pattern (Step 4b credential-write funnel lint) —
81
+ // read-only `git diff --cached --name-only` for --staged. Cannot depend on
82
+ // the TS funnel because TS is not compiled when the lint runs in pre-push.
83
+ 'scripts/lint-no-unfunneled-credential-write.js',
80
84
  // Same bootstrap-escape pattern (blocking-process-scan lint, topic 21816
81
85
  // post-mortem #3) — read-only `git diff --cached --name-only` only.
82
86
  'scripts/lint-no-blocking-process-scans.js',
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * lint-no-unfunneled-credential-write.js — refuses a raw write to the Claude Code
4
+ * credential store outside the serialization funnel (Step 4b of live credential
5
+ * re-pointing, spec §2.2).
6
+ *
7
+ * Why this exists: every in-process write to a config home's `Claude Code-credentials`
8
+ * keychain entry MUST go through `CredentialWriteFunnel.withSlotLock(slot, …)` (via
9
+ * `refreshClaudeToken`, `writeCredentialsSerialized`, or the Step-5 swap executor) so a
10
+ * refresh write can never interleave with a swap on the SAME slot and strand a rotated
11
+ * token. A callsite that writes the credential store directly — `defaultCredentialStore.write`,
12
+ * `provider.writeCredentials(...)`, or a hand-rolled `security add-generic-password` to the
13
+ * `Claude Code-credentials` service — bypasses that lock and re-opens the clobber race.
14
+ * This mirrors the SafeGitExecutor / SafeFsExecutor single-funnel precedent.
15
+ *
16
+ * Rule: outside the closed allowlist below, no source file may contain:
17
+ * - a `defaultCredentialStore.write(` call,
18
+ * - a qualified `.writeCredentials(` call (i.e. `someProvider.writeCredentials(...)` —
19
+ * the bare method DEFINITION `writeCredentials(creds…)` has no leading dot and is fine),
20
+ * - a raw `add-generic-password` invocation in a file that targets the
21
+ * `Claude Code-credentials` service (scoped by the literal service string so the OTHER
22
+ * keychain vaults — WorktreeKeyVault / SecretStore / GlobalSecretStore / RemediationKeyVault,
23
+ * each a DISTINCT service — never false-positive).
24
+ *
25
+ * Comment-only mentions are NOT a bypass and are ignored.
26
+ *
27
+ * Exit codes: 0 — clean; 1 — at least one violation.
28
+ *
29
+ * Usage:
30
+ * node scripts/lint-no-unfunneled-credential-write.js # full repo
31
+ * node scripts/lint-no-unfunneled-credential-write.js --staged # staged files
32
+ * node scripts/lint-no-unfunneled-credential-write.js <file…> # explicit files (tests)
33
+ */
34
+
35
+ import fs from 'node:fs';
36
+ import path from 'node:path';
37
+ import { execSync } from 'node:child_process';
38
+ import { fileURLToPath } from 'node:url';
39
+
40
+ const __filename = fileURLToPath(import.meta.url);
41
+ const ROOT = path.resolve(path.dirname(__filename), '..');
42
+
43
+ // ── Allowlist (closed). Each entry is a file that OWNS a credential-write primitive
44
+ // and routes it through the funnel internally (or the lint itself). Adding an entry
45
+ // requires review of WHY the callsite cannot route through `withSlotLock`. ──────
46
+ const ALLOWLIST = new Set([
47
+ // THE funnel — the per-slot lock lives here.
48
+ 'src/core/CredentialWriteFunnel.ts',
49
+ // Owns `defaultCredentialStore.write` + the raw keychain write; `refreshClaudeToken`
50
+ // wraps it in `funnel.withSlotLock(configHome, …)`.
51
+ 'src/core/OAuthRefresher.ts',
52
+ // Owns `KeychainCredentialProvider.writeCredentials` (the raw `security -i` write) +
53
+ // the sanctioned `writeCredentialsSerialized` funnel chokepoint that wraps it.
54
+ 'src/monitoring/CredentialProvider.ts',
55
+ // This lint file names the patterns it greps for.
56
+ 'scripts/lint-no-unfunneled-credential-write.js',
57
+ ]);
58
+
59
+ const SCAN_DIRS = ['src', 'scripts', 'templates'];
60
+ const EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.mjs', '.cjs', '.sh']);
61
+
62
+ // The keychain service the funnel guards. Scopes the raw `add-generic-password` rule so the
63
+ // other (distinct-service) keychain vaults never trip it.
64
+ const GUARDED_SERVICE = 'Claude Code-credentials';
65
+
66
+ const PATTERNS = [
67
+ {
68
+ re: /defaultCredentialStore\.write\s*\(/,
69
+ msg: 'direct defaultCredentialStore.write — route through CredentialWriteFunnel.withSlotLock (see refreshClaudeToken)',
70
+ },
71
+ {
72
+ re: /\.writeCredentials\s*\(/,
73
+ msg: 'direct provider.writeCredentials(...) — route through writeCredentialsSerialized (CredentialProvider.ts)',
74
+ },
75
+ ];
76
+
77
+ // A raw keychain write is flagged ONLY in a file that also references the guarded service —
78
+ // so WorktreeKeyVault / SecretStore / GlobalSecretStore / RemediationKeyVault (distinct
79
+ // services) are never false-positived.
80
+ const RAW_KEYCHAIN_WRITE = /add-generic-password/;
81
+
82
+ function isCommentLine(line) {
83
+ const t = line.trim();
84
+ return t.startsWith('//') || t.startsWith('*') || t.startsWith('/*') || t.startsWith('#');
85
+ }
86
+
87
+ function listFiles() {
88
+ if (process.argv.includes('--staged')) {
89
+ const out = execSync('git diff --cached --name-only', { cwd: ROOT, encoding: 'utf-8' });
90
+ return out.split('\n').filter(Boolean);
91
+ }
92
+ const explicit = process.argv.slice(2).filter((a) => !a.startsWith('--'));
93
+ if (explicit.length) return explicit;
94
+
95
+ const files = [];
96
+ const walk = (dir) => {
97
+ let entries;
98
+ try {
99
+ entries = fs.readdirSync(dir, { withFileTypes: true });
100
+ } catch {
101
+ return;
102
+ }
103
+ for (const e of entries) {
104
+ if (e.name === 'node_modules' || e.name === '.git' || e.name === 'dist') continue;
105
+ const full = path.join(dir, e.name);
106
+ if (e.isDirectory()) walk(full);
107
+ else if (EXTENSIONS.has(path.extname(e.name))) files.push(path.relative(ROOT, full));
108
+ }
109
+ };
110
+ for (const d of SCAN_DIRS) walk(path.join(ROOT, d));
111
+ return files;
112
+ }
113
+
114
+ let violations = 0;
115
+ for (const rel of listFiles()) {
116
+ const normalized = rel.split(path.sep).join('/');
117
+ if (ALLOWLIST.has(normalized)) continue;
118
+ if (!EXTENSIONS.has(path.extname(normalized))) continue;
119
+ const full = path.isAbsolute(rel) ? rel : path.join(ROOT, normalized);
120
+ let content;
121
+ try {
122
+ content = fs.readFileSync(full, 'utf-8');
123
+ } catch {
124
+ continue;
125
+ }
126
+ const fileTargetsGuardedService = content.includes(GUARDED_SERVICE);
127
+ const lines = content.split('\n');
128
+ for (let i = 0; i < lines.length; i++) {
129
+ const line = lines[i];
130
+ if (isCommentLine(line)) continue;
131
+ for (const { re, msg } of PATTERNS) {
132
+ if (re.test(line)) {
133
+ console.error(`${normalized}:${i + 1} — ${msg}`);
134
+ violations++;
135
+ }
136
+ }
137
+ if (fileTargetsGuardedService && RAW_KEYCHAIN_WRITE.test(line)) {
138
+ console.error(
139
+ `${normalized}:${i + 1} — raw 'add-generic-password' to the ${GUARDED_SERVICE} service outside ` +
140
+ `the funnel. Route the write through CredentialWriteFunnel.withSlotLock, or add an allowlist entry ` +
141
+ `here with a justification.`,
142
+ );
143
+ violations++;
144
+ }
145
+ }
146
+ }
147
+
148
+ if (violations > 0) {
149
+ console.error(
150
+ `\nlint-no-unfunneled-credential-write: ${violations} violation(s). Every Claude credential write ` +
151
+ `must go through CredentialWriteFunnel.withSlotLock (spec §2.2).`,
152
+ );
153
+ process.exit(1);
154
+ }
155
+ console.log('lint-no-unfunneled-credential-write: clean');