instar 0.28.102 → 0.28.103
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/hooks/instar/before-prompt-recall.js +90 -0
- package/dist/cli.js +28 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/jobMigrate.d.ts +46 -0
- package/dist/commands/jobMigrate.d.ts.map +1 -0
- package/dist/commands/jobMigrate.js +292 -0
- package/dist/commands/jobMigrate.js.map +1 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +88 -42
- package/dist/commands/server.js.map +1 -1
- package/dist/core/BackupManager.d.ts +27 -1
- package/dist/core/BackupManager.d.ts.map +1 -1
- package/dist/core/BackupManager.js +56 -1
- package/dist/core/BackupManager.js.map +1 -1
- package/dist/core/GitStateManager.d.ts +8 -0
- package/dist/core/GitStateManager.d.ts.map +1 -1
- package/dist/core/GitStateManager.js +31 -1
- package/dist/core/GitStateManager.js.map +1 -1
- package/dist/core/MigratorStepEngine.d.ts +170 -0
- package/dist/core/MigratorStepEngine.d.ts.map +1 -0
- package/dist/core/MigratorStepEngine.js +352 -0
- package/dist/core/MigratorStepEngine.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts +76 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +252 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PreCompactionFlush.d.ts +101 -0
- package/dist/core/PreCompactionFlush.d.ts.map +1 -0
- package/dist/core/PreCompactionFlush.js +282 -0
- package/dist/core/PreCompactionFlush.js.map +1 -0
- package/dist/core/PromptBuildRecall.d.ts +97 -0
- package/dist/core/PromptBuildRecall.d.ts.map +1 -0
- package/dist/core/PromptBuildRecall.js +152 -0
- package/dist/core/PromptBuildRecall.js.map +1 -0
- package/dist/core/selectIntelligenceProvider.d.ts +41 -0
- package/dist/core/selectIntelligenceProvider.d.ts.map +1 -0
- package/dist/core/selectIntelligenceProvider.js +89 -0
- package/dist/core/selectIntelligenceProvider.js.map +1 -0
- package/dist/lifeline/ServerSupervisor.d.ts +183 -0
- package/dist/lifeline/ServerSupervisor.d.ts.map +1 -1
- package/dist/lifeline/ServerSupervisor.js +443 -0
- package/dist/lifeline/ServerSupervisor.js.map +1 -1
- package/dist/memory/NativeModuleHealer.d.ts +89 -0
- package/dist/memory/NativeModuleHealer.d.ts.map +1 -1
- package/dist/memory/NativeModuleHealer.js +371 -1
- package/dist/memory/NativeModuleHealer.js.map +1 -1
- package/dist/memory/SemanticMemory.d.ts +131 -0
- package/dist/memory/SemanticMemory.d.ts.map +1 -1
- package/dist/memory/SemanticMemory.js +315 -0
- package/dist/memory/SemanticMemory.js.map +1 -1
- package/dist/messaging/DeliveryRetryManager.d.ts +60 -0
- package/dist/messaging/DeliveryRetryManager.d.ts.map +1 -1
- package/dist/messaging/DeliveryRetryManager.js +104 -0
- package/dist/messaging/DeliveryRetryManager.js.map +1 -1
- package/dist/monitoring/DegradationReporter.d.ts +164 -0
- package/dist/monitoring/DegradationReporter.d.ts.map +1 -1
- package/dist/monitoring/DegradationReporter.js +309 -1
- package/dist/monitoring/DegradationReporter.js.map +1 -1
- package/dist/monitoring/ErrorCodeExtractor.d.ts +69 -0
- package/dist/monitoring/ErrorCodeExtractor.d.ts.map +1 -0
- package/dist/monitoring/ErrorCodeExtractor.js +131 -0
- package/dist/monitoring/ErrorCodeExtractor.js.map +1 -0
- package/dist/monitoring/PromptGate.d.ts +34 -0
- package/dist/monitoring/PromptGate.d.ts.map +1 -1
- package/dist/monitoring/PromptGate.js +81 -1
- package/dist/monitoring/PromptGate.js.map +1 -1
- package/dist/monitoring/Redactor.d.ts +58 -0
- package/dist/monitoring/Redactor.d.ts.map +1 -0
- package/dist/monitoring/Redactor.js +143 -0
- package/dist/monitoring/Redactor.js.map +1 -0
- package/dist/monitoring/probes/LifelineProbe.d.ts +7 -0
- package/dist/monitoring/probes/LifelineProbe.d.ts.map +1 -1
- package/dist/monitoring/probes/LifelineProbe.js +7 -0
- package/dist/monitoring/probes/LifelineProbe.js.map +1 -1
- package/dist/monitoring/probes/__shared.d.ts +50 -0
- package/dist/monitoring/probes/__shared.d.ts.map +1 -0
- package/dist/monitoring/probes/__shared.js +51 -0
- package/dist/monitoring/probes/__shared.js.map +1 -0
- package/dist/remediation/IntentJournal.d.ts +64 -0
- package/dist/remediation/IntentJournal.d.ts.map +1 -0
- package/dist/remediation/IntentJournal.js +118 -0
- package/dist/remediation/IntentJournal.js.map +1 -0
- package/dist/remediation/MachineLock.d.ts +137 -0
- package/dist/remediation/MachineLock.d.ts.map +1 -0
- package/dist/remediation/MachineLock.js +314 -0
- package/dist/remediation/MachineLock.js.map +1 -0
- package/dist/remediation/PrimaryAggregatorLease.d.ts +205 -0
- package/dist/remediation/PrimaryAggregatorLease.d.ts.map +1 -0
- package/dist/remediation/PrimaryAggregatorLease.js +450 -0
- package/dist/remediation/PrimaryAggregatorLease.js.map +1 -0
- package/dist/remediation/RemediationContext.d.ts +63 -0
- package/dist/remediation/RemediationContext.d.ts.map +1 -0
- package/dist/remediation/RemediationContext.js +109 -0
- package/dist/remediation/RemediationContext.js.map +1 -0
- package/dist/remediation/RemediationKeyVault.d.ts +123 -0
- package/dist/remediation/RemediationKeyVault.d.ts.map +1 -0
- package/dist/remediation/RemediationKeyVault.js +564 -0
- package/dist/remediation/RemediationKeyVault.js.map +1 -0
- package/dist/remediation/Remediator.d.ts +288 -0
- package/dist/remediation/Remediator.d.ts.map +1 -0
- package/dist/remediation/Remediator.js +520 -0
- package/dist/remediation/Remediator.js.map +1 -0
- package/dist/remediation/RemediatorBootstrap.d.ts +141 -0
- package/dist/remediation/RemediatorBootstrap.d.ts.map +1 -0
- package/dist/remediation/RemediatorBootstrap.js +255 -0
- package/dist/remediation/RemediatorBootstrap.js.map +1 -0
- package/dist/remediation/TrustElevationSource.d.ts +153 -0
- package/dist/remediation/TrustElevationSource.d.ts.map +1 -0
- package/dist/remediation/TrustElevationSource.js +208 -0
- package/dist/remediation/TrustElevationSource.js.map +1 -0
- package/dist/remediation/audit/AuditProjection.d.ts +37 -0
- package/dist/remediation/audit/AuditProjection.d.ts.map +1 -0
- package/dist/remediation/audit/AuditProjection.js +118 -0
- package/dist/remediation/audit/AuditProjection.js.map +1 -0
- package/dist/remediation/audit/AuditWriter.d.ts +79 -0
- package/dist/remediation/audit/AuditWriter.d.ts.map +1 -0
- package/dist/remediation/audit/AuditWriter.js +132 -0
- package/dist/remediation/audit/AuditWriter.js.map +1 -0
- package/dist/remediation/channels/CliApprovalChannel.d.ts +37 -0
- package/dist/remediation/channels/CliApprovalChannel.d.ts.map +1 -0
- package/dist/remediation/channels/CliApprovalChannel.js +45 -0
- package/dist/remediation/channels/CliApprovalChannel.js.map +1 -0
- package/dist/remediation/channels/TelegramApprovalChannel.d.ts +37 -0
- package/dist/remediation/channels/TelegramApprovalChannel.d.ts.map +1 -0
- package/dist/remediation/channels/TelegramApprovalChannel.js +45 -0
- package/dist/remediation/channels/TelegramApprovalChannel.js.map +1 -0
- package/dist/remediation/runbooks/db-corruption.d.ts +86 -0
- package/dist/remediation/runbooks/db-corruption.d.ts.map +1 -0
- package/dist/remediation/runbooks/db-corruption.js +190 -0
- package/dist/remediation/runbooks/db-corruption.js.map +1 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.d.ts +108 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.d.ts.map +1 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.js +172 -0
- package/dist/remediation/runbooks/messaging-delivery-failed.js.map +1 -0
- package/dist/remediation/runbooks/node-abi-mismatch.d.ts +70 -0
- package/dist/remediation/runbooks/node-abi-mismatch.d.ts.map +1 -0
- package/dist/remediation/runbooks/node-abi-mismatch.js +216 -0
- package/dist/remediation/runbooks/node-abi-mismatch.js.map +1 -0
- package/dist/remediation/runbooks/supervisor-preflight.d.ts +114 -0
- package/dist/remediation/runbooks/supervisor-preflight.d.ts.map +1 -0
- package/dist/remediation/runbooks/supervisor-preflight.js +299 -0
- package/dist/remediation/runbooks/supervisor-preflight.js.map +1 -0
- package/dist/scheduler/AgentMdAtomicSave.d.ts +85 -0
- package/dist/scheduler/AgentMdAtomicSave.d.ts.map +1 -0
- package/dist/scheduler/AgentMdAtomicSave.js +169 -0
- package/dist/scheduler/AgentMdAtomicSave.js.map +1 -0
- package/dist/scheduler/AgentMdReconcile.d.ts +62 -0
- package/dist/scheduler/AgentMdReconcile.d.ts.map +1 -0
- package/dist/scheduler/AgentMdReconcile.js +212 -0
- package/dist/scheduler/AgentMdReconcile.js.map +1 -0
- package/dist/scheduler/DisabledBodyDrift.d.ts +70 -0
- package/dist/scheduler/DisabledBodyDrift.d.ts.map +1 -0
- package/dist/scheduler/DisabledBodyDrift.js +149 -0
- package/dist/scheduler/DisabledBodyDrift.js.map +1 -0
- package/dist/scheduler/InstallBuiltinJobs.d.ts +53 -0
- package/dist/scheduler/InstallBuiltinJobs.d.ts.map +1 -0
- package/dist/scheduler/InstallBuiltinJobs.js +212 -0
- package/dist/scheduler/InstallBuiltinJobs.js.map +1 -0
- package/dist/scheduler/JobLoader.d.ts.map +1 -1
- package/dist/scheduler/JobLoader.js +48 -0
- package/dist/scheduler/JobLoader.js.map +1 -1
- package/dist/scheduler/MigrationInvariants.d.ts +62 -0
- package/dist/scheduler/MigrationInvariants.d.ts.map +1 -0
- package/dist/scheduler/MigrationInvariants.js +215 -0
- package/dist/scheduler/MigrationInvariants.js.map +1 -0
- package/dist/scheduler/MigrationLedger.d.ts +84 -0
- package/dist/scheduler/MigrationLedger.d.ts.map +1 -0
- package/dist/scheduler/MigrationLedger.js +114 -0
- package/dist/scheduler/MigrationLedger.js.map +1 -0
- package/dist/server/fileRoutes.d.ts.map +1 -1
- package/dist/server/fileRoutes.js +7 -0
- package/dist/server/fileRoutes.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +141 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +3 -2
- package/scripts/classify-default-drift.mjs +238 -0
- package/scripts/eli16-overview-check.mjs +85 -0
- package/scripts/instar-dev-precommit.js +60 -1
- package/scripts/lint-degradation-emit-sites.js +116 -0
- package/scripts/lint-no-direct-destructive.js +12 -0
- package/scripts/protect-migration-guarantee.js +61 -0
- package/scripts/regen-default-job-templates.mjs +111 -0
- package/scripts/verify-runbook-pr-signature.js +366 -0
- package/src/data/builtin-manifest.json +93 -93
- package/src/scaffold/bootstrap.ts +138 -0
- package/src/scaffold/keys/instar-release-pub.pem +3 -0
- package/src/scaffold/templates/jobs/instar/.gitkeep +0 -0
- package/src/scaffold/templates/jobs/instar/commitment-detection.md +28 -0
- package/src/scaffold/templates/jobs/instar/evolution-overdue-check.md +28 -0
- package/src/scaffold/templates/jobs/instar/evolution-proposal-evaluate.md +30 -0
- package/src/scaffold/templates/jobs/instar/evolution-proposal-implement.md +25 -0
- package/src/scaffold/templates/jobs/instar/health-check.md +14 -0
- package/src/scaffold/templates/jobs/instar/identity-review.md +42 -0
- package/src/scaffold/templates/jobs/instar/insight-harvest.md +31 -0
- package/src/scaffold/templates/jobs/instar/overseer-development.md +23 -0
- package/src/scaffold/templates/jobs/instar/overseer-guardian.md +28 -0
- package/src/scaffold/templates/jobs/instar/overseer-infrastructure.md +26 -0
- package/src/scaffold/templates/jobs/instar/overseer-learning.md +28 -0
- package/src/scaffold/templates/jobs/instar/overseer-maintenance.md +27 -0
- package/src/scaffold/templates/jobs/instar/reflection-trigger.md +40 -0
- package/src/scaffold/templates/jobs/instar/relationship-maintenance.md +16 -0
- package/src/scaffold/templates.ts +1616 -0
- package/upgrades/0.28.102.md +6 -7
- package/upgrades/0.28.103.md +471 -0
- package/upgrades/side-effects/a47-primary-aggregator-lease.md +82 -0
- package/upgrades/side-effects/agentmd-atomic-save.md +66 -0
- package/upgrades/side-effects/agentmd-loader-200jobs-perf-benchmark.md +50 -0
- package/upgrades/side-effects/agentmd-reconcile-boot-lifecycle.md +66 -0
- package/upgrades/side-effects/api-safety-guard.md +80 -0
- package/upgrades/side-effects/c1-runbook-pr-gate.md +83 -0
- package/upgrades/side-effects/disabled-body-drift-detection.md +59 -0
- package/upgrades/side-effects/drift-classifier-release-time.md +71 -0
- package/upgrades/side-effects/eli16-overview-required-gate.md +77 -0
- package/upgrades/side-effects/f1-remediation-key-vault.md +195 -0
- package/upgrades/side-effects/f2-redactor-errorcode-extractor.md +130 -0
- package/upgrades/side-effects/f3-degradation-reporter-shim.md +105 -0
- package/upgrades/side-effects/f4-machine-locks-intent-journal-audit.md +117 -0
- package/upgrades/side-effects/f5-trust-elevation-source.md +258 -0
- package/upgrades/side-effects/f6-supervisor-handshake.md +234 -0
- package/upgrades/side-effects/f7-post-update-migrator-atomic-step.md +129 -0
- package/upgrades/side-effects/f8-remediator-skeleton.md +153 -0
- package/upgrades/side-effects/f8-rest-enforcement.md +72 -0
- package/upgrades/side-effects/file-viewer-jobs-instar-never-editable.md +41 -0
- package/upgrades/side-effects/frontmatter-interpolation-breakout-test.md +48 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-2.md +78 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-3.md +76 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-4-endpoints.md +62 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-5.md +67 -0
- package/upgrades/side-effects/jobs-as-agentmd-phase-6.md +53 -0
- package/upgrades/side-effects/jobs-endpoints-auth-verification.md +52 -0
- package/upgrades/side-effects/migration-guarantee-suite.md +61 -0
- package/upgrades/side-effects/migration-runtime-invariant-gate.md +59 -0
- package/upgrades/side-effects/migration-telemetry-invariant-8.md +68 -0
- package/upgrades/side-effects/npm-pack-templates-smoke.md +51 -0
- package/upgrades/side-effects/openclaw-import-before-prompt-build.md +84 -0
- package/upgrades/side-effects/openclaw-import-pre-compaction-flush.md +85 -0
- package/upgrades/side-effects/prompt-gate-no-prompt-cache.md +258 -0
- package/upgrades/side-effects/tier2-degradation-reporter-live-wire.md +146 -0
- package/upgrades/side-effects/w1-node-abi-mismatch-runbook.md +154 -0
- package/upgrades/side-effects/w2-supervisor-preflight-runbook.md +176 -0
- package/upgrades/side-effects/w3-messaging-delivery-failed-runbook.md +185 -0
- package/upgrades/side-effects/w4-db-corruption-runbook.md +114 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreCompactionFlush — Save important context to MEMORY.md *before* Claude Code
|
|
3
|
+
* compaction collapses working memory.
|
|
4
|
+
*
|
|
5
|
+
* Architecture (Option A — server-side, agent-noninterruptive):
|
|
6
|
+
* 1. Claude Code emits PreCompact hook → instar's hook-event-reporter POSTs
|
|
7
|
+
* to /hooks/events → HookEventReceiver emits 'PreCompact' event.
|
|
8
|
+
* 2. This class listens on that event. On each fire, it:
|
|
9
|
+
* a. Locates the agent's transcript jsonl using Claude Code's standard
|
|
10
|
+
* path convention: ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
|
|
11
|
+
* b. Reads the last N chars of the transcript (default 30KB) — bounded
|
|
12
|
+
* to keep the LLM call cheap.
|
|
13
|
+
* c. Sends a fact-extraction prompt to the shared intelligence provider
|
|
14
|
+
* (subscription path, no per-call cost on default config).
|
|
15
|
+
* d. Parses the response. Acceptable shapes: JSON array, JSON object
|
|
16
|
+
* with `facts` array, or markdown bullet list. Any valid fact has
|
|
17
|
+
* `slug` (kebab-case, ≤48 chars) and `body` (≤500 chars).
|
|
18
|
+
* e. For each parsed fact, writes a new file under
|
|
19
|
+
* `<projectDir>/.instar/memory/learning_<ts>_<slug>.md` with the
|
|
20
|
+
* standard memory frontmatter (type=learning).
|
|
21
|
+
* f. Appends a one-line index entry to `<projectDir>/.instar/MEMORY.md`
|
|
22
|
+
* if a "## Learnings" or similar section exists (best-effort).
|
|
23
|
+
* g. Writes a structured audit entry to
|
|
24
|
+
* `<projectDir>/.instar/audit/pre-compaction-flush.jsonl`.
|
|
25
|
+
*
|
|
26
|
+
* Safety properties:
|
|
27
|
+
* - Default `enabled: false` — flush is opt-in. The behavior change is
|
|
28
|
+
* additive and the audit log surfaces every fire, so operators can
|
|
29
|
+
* observe before enabling.
|
|
30
|
+
* - `maxFactsPerFlush` (default 5) caps the blast radius if the LLM
|
|
31
|
+
* hallucinates a long list.
|
|
32
|
+
* - All writes are best-effort: any single failure (transcript missing,
|
|
33
|
+
* LLM error, file write race) is audited and returns; never throws.
|
|
34
|
+
* - Calls run async/detached from the hook caller. The hook itself
|
|
35
|
+
* returns immediately so compaction is never delayed.
|
|
36
|
+
*
|
|
37
|
+
* Spec: docs/specs/OPENCLAW-IMPORT-PRE-COMPACTION-FLUSH-SPEC.md
|
|
38
|
+
*/
|
|
39
|
+
import fs from 'node:fs';
|
|
40
|
+
import path from 'node:path';
|
|
41
|
+
import os from 'node:os';
|
|
42
|
+
import crypto from 'node:crypto';
|
|
43
|
+
export const DEFAULT_PRE_COMPACTION_FLUSH_CONFIG = {
|
|
44
|
+
enabled: false,
|
|
45
|
+
maxFactsPerFlush: 5,
|
|
46
|
+
transcriptCharBudget: 30_000,
|
|
47
|
+
};
|
|
48
|
+
export class PreCompactionFlush {
|
|
49
|
+
deps;
|
|
50
|
+
config;
|
|
51
|
+
constructor(deps, config) {
|
|
52
|
+
this.deps = deps;
|
|
53
|
+
this.config = config;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Handle a PreCompact event. Fire-and-forget; never throws to the caller.
|
|
57
|
+
* Returns the audit entry it wrote (for tests).
|
|
58
|
+
*/
|
|
59
|
+
async handle(payload) {
|
|
60
|
+
const startedAt = (this.deps.now?.() ?? new Date()).getTime();
|
|
61
|
+
const flushId = `flush_${crypto.randomBytes(6).toString('hex')}`;
|
|
62
|
+
const sessionId = payload.session_id ?? '';
|
|
63
|
+
const trigger = payload.trigger;
|
|
64
|
+
const audit = (outcome, extra) => {
|
|
65
|
+
const entry = {
|
|
66
|
+
flushId,
|
|
67
|
+
sessionId,
|
|
68
|
+
trigger,
|
|
69
|
+
at: (this.deps.now?.() ?? new Date()).toISOString(),
|
|
70
|
+
outcome,
|
|
71
|
+
durationMs: (this.deps.now?.() ?? new Date()).getTime() - startedAt,
|
|
72
|
+
...extra,
|
|
73
|
+
};
|
|
74
|
+
this.writeAudit(entry);
|
|
75
|
+
return entry;
|
|
76
|
+
};
|
|
77
|
+
if (!this.config.enabled)
|
|
78
|
+
return audit('disabled');
|
|
79
|
+
if (!this.deps.intelligence)
|
|
80
|
+
return audit('no-intelligence');
|
|
81
|
+
if (!sessionId)
|
|
82
|
+
return audit('no-session-id');
|
|
83
|
+
const transcriptPath = this.resolveTranscriptPath(payload);
|
|
84
|
+
const transcriptTail = this.readTranscriptTail(transcriptPath);
|
|
85
|
+
if (!transcriptTail)
|
|
86
|
+
return audit('no-transcript', { reason: `path: ${transcriptPath}` });
|
|
87
|
+
let llmResponse;
|
|
88
|
+
try {
|
|
89
|
+
llmResponse = await this.deps.intelligence.evaluate(this.buildPrompt(transcriptTail), { maxTokens: 800, temperature: 0 });
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return audit('provider-error', { reason: String(err).slice(0, 200) });
|
|
93
|
+
}
|
|
94
|
+
const facts = this.parseFacts(llmResponse).slice(0, this.config.maxFactsPerFlush);
|
|
95
|
+
if (facts.length === 0) {
|
|
96
|
+
// Distinguish "LLM returned NONE / empty list" from "couldn't parse".
|
|
97
|
+
if (/^\s*NONE\s*$/i.test(llmResponse) || /^\s*\[\s*\]\s*$/.test(llmResponse)) {
|
|
98
|
+
return audit('no-facts');
|
|
99
|
+
}
|
|
100
|
+
return audit('parse-failure', { reason: llmResponse.slice(0, 200) });
|
|
101
|
+
}
|
|
102
|
+
let written = 0;
|
|
103
|
+
try {
|
|
104
|
+
for (const fact of facts) {
|
|
105
|
+
if (this.writeFact(flushId, fact))
|
|
106
|
+
written++;
|
|
107
|
+
}
|
|
108
|
+
this.appendMemoryIndex(facts, flushId);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
return audit('write-error', { reason: String(err).slice(0, 200), factsWritten: written });
|
|
112
|
+
}
|
|
113
|
+
return audit('ok', { factsWritten: written });
|
|
114
|
+
}
|
|
115
|
+
/** Build the flush prompt. Exported as a member for test inspection. */
|
|
116
|
+
buildPrompt(transcriptTail) {
|
|
117
|
+
return [
|
|
118
|
+
'You are an instar agent about to undergo context compaction. Recent conversation will be',
|
|
119
|
+
'collapsed into a generic summary. Identify 0-5 DURABLE facts from the recent conversation',
|
|
120
|
+
'that should survive compaction by being written to MEMORY.md as learnings.',
|
|
121
|
+
'',
|
|
122
|
+
'A durable fact is:',
|
|
123
|
+
' - specific (not "we talked about X")',
|
|
124
|
+
' - actionable for a future session ("X is at path Y", "approach Z works for case W")',
|
|
125
|
+
' - not already in your code or git history (those are recoverable from disk)',
|
|
126
|
+
' - not ephemeral state ("I am about to commit", "I am waiting for CI")',
|
|
127
|
+
'',
|
|
128
|
+
'Respond with a JSON array of objects, each with:',
|
|
129
|
+
' - "slug": kebab-case, max 48 chars, identifies the fact uniquely',
|
|
130
|
+
' - "body": 1-3 sentences, max 500 chars',
|
|
131
|
+
'',
|
|
132
|
+
'If nothing durable surfaces, respond with the literal token: NONE',
|
|
133
|
+
'',
|
|
134
|
+
'Recent conversation (tail):',
|
|
135
|
+
'---',
|
|
136
|
+
transcriptTail,
|
|
137
|
+
'---',
|
|
138
|
+
'',
|
|
139
|
+
'Respond with the JSON array or NONE. No other text.',
|
|
140
|
+
].join('\n');
|
|
141
|
+
}
|
|
142
|
+
/** Parse the LLM response into facts. Accepts JSON array, JSON object with `facts`, or markdown. */
|
|
143
|
+
parseFacts(response) {
|
|
144
|
+
const trimmed = response.trim();
|
|
145
|
+
if (/^\s*NONE\s*$/i.test(trimmed))
|
|
146
|
+
return [];
|
|
147
|
+
// Try fenced JSON first.
|
|
148
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*([\s\S]+?)\s*```/);
|
|
149
|
+
const candidate = fenceMatch ? fenceMatch[1] : trimmed;
|
|
150
|
+
try {
|
|
151
|
+
const parsed = JSON.parse(candidate);
|
|
152
|
+
const arr = Array.isArray(parsed)
|
|
153
|
+
? parsed
|
|
154
|
+
: (parsed && typeof parsed === 'object' && Array.isArray(parsed.facts)
|
|
155
|
+
? parsed.facts
|
|
156
|
+
: null);
|
|
157
|
+
if (!arr)
|
|
158
|
+
return [];
|
|
159
|
+
const facts = [];
|
|
160
|
+
for (const item of arr) {
|
|
161
|
+
const fact = this.coerceFact(item);
|
|
162
|
+
if (fact)
|
|
163
|
+
facts.push(fact);
|
|
164
|
+
}
|
|
165
|
+
return facts;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
coerceFact(item) {
|
|
172
|
+
if (!item || typeof item !== 'object')
|
|
173
|
+
return null;
|
|
174
|
+
const obj = item;
|
|
175
|
+
const rawSlug = typeof obj.slug === 'string' ? obj.slug : null;
|
|
176
|
+
const rawBody = typeof obj.body === 'string' ? obj.body : null;
|
|
177
|
+
if (!rawSlug || !rawBody)
|
|
178
|
+
return null;
|
|
179
|
+
const slug = rawSlug
|
|
180
|
+
.toLowerCase()
|
|
181
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
182
|
+
.replace(/^-+|-+$/g, '')
|
|
183
|
+
.slice(0, 48);
|
|
184
|
+
if (!slug)
|
|
185
|
+
return null;
|
|
186
|
+
const body = rawBody.trim().slice(0, 500);
|
|
187
|
+
if (!body)
|
|
188
|
+
return null;
|
|
189
|
+
return { slug, body };
|
|
190
|
+
}
|
|
191
|
+
resolveTranscriptPath(payload) {
|
|
192
|
+
if (payload.transcript_path)
|
|
193
|
+
return payload.transcript_path;
|
|
194
|
+
if (!payload.session_id)
|
|
195
|
+
return '';
|
|
196
|
+
const root = this.deps.claudeProjectsRoot ?? path.join(os.homedir(), '.claude', 'projects');
|
|
197
|
+
const encoded = this.deps.projectDir.replace(/[\/.]/g, '-');
|
|
198
|
+
return path.join(root, encoded, `${payload.session_id}.jsonl`);
|
|
199
|
+
}
|
|
200
|
+
readTranscriptTail(transcriptPath) {
|
|
201
|
+
if (!transcriptPath || !fs.existsSync(transcriptPath))
|
|
202
|
+
return null;
|
|
203
|
+
try {
|
|
204
|
+
const stat = fs.statSync(transcriptPath);
|
|
205
|
+
const budget = this.config.transcriptCharBudget;
|
|
206
|
+
if (stat.size <= budget) {
|
|
207
|
+
return fs.readFileSync(transcriptPath, 'utf8');
|
|
208
|
+
}
|
|
209
|
+
const fd = fs.openSync(transcriptPath, 'r');
|
|
210
|
+
const buf = Buffer.alloc(budget);
|
|
211
|
+
fs.readSync(fd, buf, 0, budget, stat.size - budget);
|
|
212
|
+
fs.closeSync(fd);
|
|
213
|
+
// Drop leading partial line so we start on a complete event boundary.
|
|
214
|
+
const text = buf.toString('utf8');
|
|
215
|
+
const firstNl = text.indexOf('\n');
|
|
216
|
+
return firstNl >= 0 ? text.slice(firstNl + 1) : text;
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
writeFact(flushId, fact) {
|
|
223
|
+
try {
|
|
224
|
+
const ts = (this.deps.now?.() ?? new Date()).toISOString().replace(/[:.]/g, '-');
|
|
225
|
+
const memoryDir = path.join(this.deps.projectDir, '.instar', 'memory');
|
|
226
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
227
|
+
const file = path.join(memoryDir, `learning_precompact_${ts}_${fact.slug}.md`);
|
|
228
|
+
const body = [
|
|
229
|
+
'---',
|
|
230
|
+
`name: precompact-${fact.slug}`,
|
|
231
|
+
`description: ${fact.body.split('\n')[0].slice(0, 120)}`,
|
|
232
|
+
'metadata:',
|
|
233
|
+
' type: learning',
|
|
234
|
+
` source: pre-compaction-flush`,
|
|
235
|
+
` flushId: ${flushId}`,
|
|
236
|
+
'---',
|
|
237
|
+
'',
|
|
238
|
+
fact.body,
|
|
239
|
+
'',
|
|
240
|
+
].join('\n');
|
|
241
|
+
fs.writeFileSync(file, body);
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
appendMemoryIndex(facts, flushId) {
|
|
249
|
+
try {
|
|
250
|
+
const indexPath = path.join(this.deps.projectDir, '.instar', 'MEMORY.md');
|
|
251
|
+
if (!fs.existsSync(indexPath))
|
|
252
|
+
return;
|
|
253
|
+
const existing = fs.readFileSync(indexPath, 'utf8');
|
|
254
|
+
const newLines = facts.map((f) => `- [precompact: ${f.slug}](memory/learning_precompact_*_${f.slug}.md) — ${f.body.split('\n')[0].slice(0, 120)} (flush ${flushId})`);
|
|
255
|
+
// Append under a "## Pre-Compaction Saves" section, creating it if missing.
|
|
256
|
+
const SECTION = '## Pre-Compaction Saves';
|
|
257
|
+
let updated;
|
|
258
|
+
if (existing.includes(SECTION)) {
|
|
259
|
+
updated = existing.replace(new RegExp(`(${SECTION}[^\n]*\n)`), (m) => `${m}${newLines.join('\n')}\n`);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
updated = existing.trimEnd() + `\n\n${SECTION}\n\n${newLines.join('\n')}\n`;
|
|
263
|
+
}
|
|
264
|
+
fs.writeFileSync(indexPath, updated);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
// Best effort.
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
writeAudit(entry) {
|
|
271
|
+
try {
|
|
272
|
+
const auditDir = path.join(this.deps.projectDir, '.instar', 'audit');
|
|
273
|
+
fs.mkdirSync(auditDir, { recursive: true });
|
|
274
|
+
const auditPath = path.join(auditDir, 'pre-compaction-flush.jsonl');
|
|
275
|
+
fs.appendFileSync(auditPath, JSON.stringify(entry) + '\n');
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Audit must never throw.
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=PreCompactionFlush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PreCompactionFlush.js","sourceRoot":"","sources":["../../src/core/PreCompactionFlush.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptBuildRecall — bounded pre-reply memory recall, imported from OpenClaw's
|
|
3
|
+
* `before_prompt_build` hook pattern.
|
|
4
|
+
*
|
|
5
|
+
* OpenClaw's active-memory plugin runs a recall pass before every eligible
|
|
6
|
+
* prompt and injects the result as a system-prompt prefix. The shape Instar
|
|
7
|
+
* adopts here:
|
|
8
|
+
*
|
|
9
|
+
* 1. A typed primitive (`PromptBuildRecall.recall()`) that takes the user's
|
|
10
|
+
* message + a session id, returns a context-text string to inject.
|
|
11
|
+
* 2. Bounded blast radius: result is capped at `maxRecallChars` (default
|
|
12
|
+
* 1200), pulls at most `maxRecallResults` entries (default 5).
|
|
13
|
+
* 3. Per-call timeout (default 2000 ms — the recall runs synchronously inside
|
|
14
|
+
* a Claude Code UserPromptSubmit hook, so it must be fast).
|
|
15
|
+
* 4. Cache: identical (user-message, agent) pairs dedupe with a `cacheTtlMs`
|
|
16
|
+
* window (default 15 s). Repeated rapid prompts don't re-search.
|
|
17
|
+
* 5. Circuit breaker: after `circuitBreakerMaxFailures` consecutive errors
|
|
18
|
+
* (default 3) the breaker opens for `circuitBreakerCooldownMs` (default
|
|
19
|
+
* 60 s) and recall short-circuits to empty.
|
|
20
|
+
* 6. Tool allowlist: the OpenClaw pattern restricts the recall sub-agent to
|
|
21
|
+
* `[memory_recall, memory_search, memory_get]`. Here there's no
|
|
22
|
+
* sub-agent — recall calls `SemanticMemory.search` directly. Implicit
|
|
23
|
+
* allowlist: read-only memory queries, never anything else.
|
|
24
|
+
*
|
|
25
|
+
* Spec: docs/specs/OPENCLAW-IMPORT-BEFORE-PROMPT-BUILD-SPEC.md.
|
|
26
|
+
*/
|
|
27
|
+
import type { SemanticMemory } from '../memory/SemanticMemory.js';
|
|
28
|
+
export interface PromptBuildRecallConfig {
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
/** Hard cap on the size of the injected context block. Default 1200. */
|
|
31
|
+
maxRecallChars: number;
|
|
32
|
+
/** Hard cap on the number of memory entries pulled. Default 5. */
|
|
33
|
+
maxRecallResults: number;
|
|
34
|
+
/** TTL for the in-process recall cache. Default 15 000 ms. */
|
|
35
|
+
cacheTtlMs: number;
|
|
36
|
+
/** Consecutive failures before the circuit opens. Default 3. */
|
|
37
|
+
circuitBreakerMaxFailures: number;
|
|
38
|
+
/** Cooldown after the circuit opens. Default 60 000 ms. */
|
|
39
|
+
circuitBreakerCooldownMs: number;
|
|
40
|
+
/** Per-call timeout in ms. Default 2 000 ms. Recall is on the hot path. */
|
|
41
|
+
recallTimeoutMs: number;
|
|
42
|
+
/** Minimum confidence filter passed to SemanticMemory.search. Default 0.5. */
|
|
43
|
+
minConfidence: number;
|
|
44
|
+
}
|
|
45
|
+
export declare const DEFAULT_PROMPT_BUILD_RECALL_CONFIG: PromptBuildRecallConfig;
|
|
46
|
+
export interface PromptBuildRecallDeps {
|
|
47
|
+
semanticMemory: SemanticMemory | null;
|
|
48
|
+
now?: () => number;
|
|
49
|
+
}
|
|
50
|
+
export type PromptBuildRecallSource = 'fresh' | 'cached' | 'disabled' | 'no-memory' | 'circuit-open' | 'timeout' | 'empty' | 'error';
|
|
51
|
+
export interface PromptBuildRecallResult {
|
|
52
|
+
contextText: string;
|
|
53
|
+
source: PromptBuildRecallSource;
|
|
54
|
+
elapsedMs: number;
|
|
55
|
+
resultsCount: number;
|
|
56
|
+
cacheKey: string;
|
|
57
|
+
}
|
|
58
|
+
interface CircuitState {
|
|
59
|
+
consecutiveFailures: number;
|
|
60
|
+
openUntil: number;
|
|
61
|
+
}
|
|
62
|
+
export declare class PromptBuildRecall {
|
|
63
|
+
private readonly deps;
|
|
64
|
+
private readonly config;
|
|
65
|
+
private readonly cache;
|
|
66
|
+
private circuit;
|
|
67
|
+
constructor(deps: PromptBuildRecallDeps, config: PromptBuildRecallConfig);
|
|
68
|
+
/**
|
|
69
|
+
* Run a recall pass for a user message. Returns the context-text to inject
|
|
70
|
+
* into the system prompt (or empty string if nothing useful surfaces).
|
|
71
|
+
*
|
|
72
|
+
* Synchronous because the caller (UserPromptSubmit hook) blocks on the
|
|
73
|
+
* response. The recall path itself uses sqlite-backed SemanticMemory which
|
|
74
|
+
* is fast and synchronous; the only "async" guard is the timeout, which is
|
|
75
|
+
* enforced by checking elapsed time after the search.
|
|
76
|
+
*/
|
|
77
|
+
recall(opts: {
|
|
78
|
+
userMessage: string;
|
|
79
|
+
sessionId?: string;
|
|
80
|
+
}): PromptBuildRecallResult;
|
|
81
|
+
/**
|
|
82
|
+
* Format the recall context block. Capped at `maxRecallChars`. Each entry
|
|
83
|
+
* gets a single line: "- <name>: <description-first-line>". Entries are
|
|
84
|
+
* dropped (not truncated) if adding them would exceed the cap.
|
|
85
|
+
*/
|
|
86
|
+
private formatContextBlock;
|
|
87
|
+
private makeCacheKey;
|
|
88
|
+
private now;
|
|
89
|
+
/** Test hook: inspect the cache without exporting Map. */
|
|
90
|
+
getCacheSize(): number;
|
|
91
|
+
/** Test hook: inspect the circuit. */
|
|
92
|
+
getCircuitState(): Readonly<CircuitState>;
|
|
93
|
+
/** Test hook: clear cache and circuit. */
|
|
94
|
+
reset(): void;
|
|
95
|
+
}
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=PromptBuildRecall.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromptBuildRecall.d.ts","sourceRoot":"","sources":["../../src/core/PromptBuildRecall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,gBAAgB,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,yBAAyB,EAAE,MAAM,CAAC;IAClC,2DAA2D;IAC3D,wBAAwB,EAAE,MAAM,CAAC;IACjC,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,kCAAkC,EAAE,uBAShD,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,uBAAuB,GAC/B,OAAO,GACP,QAAQ,GACR,UAAU,GACV,WAAW,GACX,cAAc,GACd,SAAS,GACT,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,uBAAuB,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,UAAU,YAAY;IACpB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAwB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,OAAO,CAA0D;gBAE7D,IAAI,EAAE,qBAAqB,EAAE,MAAM,EAAE,uBAAuB;IAKxE;;;;;;;;OAQG;IACH,MAAM,CAAC,IAAI,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,uBAAuB;IA8DlF;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,GAAG;IAIX,0DAA0D;IAC1D,YAAY,IAAI,MAAM;IAItB,sCAAsC;IACtC,eAAe,IAAI,QAAQ,CAAC,YAAY,CAAC;IAIzC,0CAA0C;IAC1C,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PromptBuildRecall — bounded pre-reply memory recall, imported from OpenClaw's
|
|
3
|
+
* `before_prompt_build` hook pattern.
|
|
4
|
+
*
|
|
5
|
+
* OpenClaw's active-memory plugin runs a recall pass before every eligible
|
|
6
|
+
* prompt and injects the result as a system-prompt prefix. The shape Instar
|
|
7
|
+
* adopts here:
|
|
8
|
+
*
|
|
9
|
+
* 1. A typed primitive (`PromptBuildRecall.recall()`) that takes the user's
|
|
10
|
+
* message + a session id, returns a context-text string to inject.
|
|
11
|
+
* 2. Bounded blast radius: result is capped at `maxRecallChars` (default
|
|
12
|
+
* 1200), pulls at most `maxRecallResults` entries (default 5).
|
|
13
|
+
* 3. Per-call timeout (default 2000 ms — the recall runs synchronously inside
|
|
14
|
+
* a Claude Code UserPromptSubmit hook, so it must be fast).
|
|
15
|
+
* 4. Cache: identical (user-message, agent) pairs dedupe with a `cacheTtlMs`
|
|
16
|
+
* window (default 15 s). Repeated rapid prompts don't re-search.
|
|
17
|
+
* 5. Circuit breaker: after `circuitBreakerMaxFailures` consecutive errors
|
|
18
|
+
* (default 3) the breaker opens for `circuitBreakerCooldownMs` (default
|
|
19
|
+
* 60 s) and recall short-circuits to empty.
|
|
20
|
+
* 6. Tool allowlist: the OpenClaw pattern restricts the recall sub-agent to
|
|
21
|
+
* `[memory_recall, memory_search, memory_get]`. Here there's no
|
|
22
|
+
* sub-agent — recall calls `SemanticMemory.search` directly. Implicit
|
|
23
|
+
* allowlist: read-only memory queries, never anything else.
|
|
24
|
+
*
|
|
25
|
+
* Spec: docs/specs/OPENCLAW-IMPORT-BEFORE-PROMPT-BUILD-SPEC.md.
|
|
26
|
+
*/
|
|
27
|
+
import crypto from 'node:crypto';
|
|
28
|
+
export const DEFAULT_PROMPT_BUILD_RECALL_CONFIG = {
|
|
29
|
+
enabled: false,
|
|
30
|
+
maxRecallChars: 1200,
|
|
31
|
+
maxRecallResults: 5,
|
|
32
|
+
cacheTtlMs: 15_000,
|
|
33
|
+
circuitBreakerMaxFailures: 3,
|
|
34
|
+
circuitBreakerCooldownMs: 60_000,
|
|
35
|
+
recallTimeoutMs: 2_000,
|
|
36
|
+
minConfidence: 0.5,
|
|
37
|
+
};
|
|
38
|
+
export class PromptBuildRecall {
|
|
39
|
+
deps;
|
|
40
|
+
config;
|
|
41
|
+
cache = new Map();
|
|
42
|
+
circuit = { consecutiveFailures: 0, openUntil: 0 };
|
|
43
|
+
constructor(deps, config) {
|
|
44
|
+
this.deps = deps;
|
|
45
|
+
this.config = config;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Run a recall pass for a user message. Returns the context-text to inject
|
|
49
|
+
* into the system prompt (or empty string if nothing useful surfaces).
|
|
50
|
+
*
|
|
51
|
+
* Synchronous because the caller (UserPromptSubmit hook) blocks on the
|
|
52
|
+
* response. The recall path itself uses sqlite-backed SemanticMemory which
|
|
53
|
+
* is fast and synchronous; the only "async" guard is the timeout, which is
|
|
54
|
+
* enforced by checking elapsed time after the search.
|
|
55
|
+
*/
|
|
56
|
+
recall(opts) {
|
|
57
|
+
const start = this.now();
|
|
58
|
+
const cacheKey = this.makeCacheKey(opts);
|
|
59
|
+
if (!this.config.enabled) {
|
|
60
|
+
return { contextText: '', source: 'disabled', elapsedMs: 0, resultsCount: 0, cacheKey };
|
|
61
|
+
}
|
|
62
|
+
// Cache hit?
|
|
63
|
+
const cached = this.cache.get(cacheKey);
|
|
64
|
+
if (cached && cached.expiresAt > start) {
|
|
65
|
+
return { ...cached.value, source: 'cached', elapsedMs: this.now() - start };
|
|
66
|
+
}
|
|
67
|
+
// Circuit breaker open?
|
|
68
|
+
if (this.circuit.openUntil > start) {
|
|
69
|
+
return { contextText: '', source: 'circuit-open', elapsedMs: 0, resultsCount: 0, cacheKey };
|
|
70
|
+
}
|
|
71
|
+
if (!this.deps.semanticMemory) {
|
|
72
|
+
return { contextText: '', source: 'no-memory', elapsedMs: 0, resultsCount: 0, cacheKey };
|
|
73
|
+
}
|
|
74
|
+
let entries = [];
|
|
75
|
+
try {
|
|
76
|
+
const raw = this.deps.semanticMemory.search(opts.userMessage, {
|
|
77
|
+
limit: this.config.maxRecallResults,
|
|
78
|
+
minConfidence: this.config.minConfidence,
|
|
79
|
+
});
|
|
80
|
+
entries = raw;
|
|
81
|
+
this.circuit.consecutiveFailures = 0;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
this.circuit.consecutiveFailures++;
|
|
85
|
+
if (this.circuit.consecutiveFailures >= this.config.circuitBreakerMaxFailures) {
|
|
86
|
+
this.circuit.openUntil = start + this.config.circuitBreakerCooldownMs;
|
|
87
|
+
}
|
|
88
|
+
return { contextText: '', source: 'error', elapsedMs: this.now() - start, resultsCount: 0, cacheKey };
|
|
89
|
+
}
|
|
90
|
+
const elapsed = this.now() - start;
|
|
91
|
+
if (elapsed > this.config.recallTimeoutMs) {
|
|
92
|
+
return { contextText: '', source: 'timeout', elapsedMs: elapsed, resultsCount: 0, cacheKey };
|
|
93
|
+
}
|
|
94
|
+
if (entries.length === 0) {
|
|
95
|
+
const result = { contextText: '', source: 'empty', elapsedMs: elapsed, resultsCount: 0, cacheKey };
|
|
96
|
+
this.cache.set(cacheKey, { value: result, expiresAt: start + this.config.cacheTtlMs });
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
const contextText = this.formatContextBlock(entries);
|
|
100
|
+
const result = {
|
|
101
|
+
contextText,
|
|
102
|
+
source: 'fresh',
|
|
103
|
+
elapsedMs: elapsed,
|
|
104
|
+
resultsCount: entries.length,
|
|
105
|
+
cacheKey,
|
|
106
|
+
};
|
|
107
|
+
this.cache.set(cacheKey, { value: result, expiresAt: start + this.config.cacheTtlMs });
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Format the recall context block. Capped at `maxRecallChars`. Each entry
|
|
112
|
+
* gets a single line: "- <name>: <description-first-line>". Entries are
|
|
113
|
+
* dropped (not truncated) if adding them would exceed the cap.
|
|
114
|
+
*/
|
|
115
|
+
formatContextBlock(entries) {
|
|
116
|
+
const header = '<active_memory_recall>';
|
|
117
|
+
const footer = '</active_memory_recall>';
|
|
118
|
+
const lines = [header];
|
|
119
|
+
let used = header.length + footer.length + 2; // \n boundaries
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
const desc = (entry.description ?? '').split('\n')[0].trim();
|
|
122
|
+
const line = desc ? `- ${entry.name}: ${desc}` : `- ${entry.name}`;
|
|
123
|
+
if (used + line.length + 1 > this.config.maxRecallChars)
|
|
124
|
+
break;
|
|
125
|
+
lines.push(line);
|
|
126
|
+
used += line.length + 1;
|
|
127
|
+
}
|
|
128
|
+
lines.push(footer);
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
makeCacheKey(opts) {
|
|
132
|
+
const normalized = opts.userMessage.trim().toLowerCase().slice(0, 500);
|
|
133
|
+
return crypto.createHash('sha1').update(normalized).digest('hex').slice(0, 16);
|
|
134
|
+
}
|
|
135
|
+
now() {
|
|
136
|
+
return this.deps.now?.() ?? Date.now();
|
|
137
|
+
}
|
|
138
|
+
/** Test hook: inspect the cache without exporting Map. */
|
|
139
|
+
getCacheSize() {
|
|
140
|
+
return this.cache.size;
|
|
141
|
+
}
|
|
142
|
+
/** Test hook: inspect the circuit. */
|
|
143
|
+
getCircuitState() {
|
|
144
|
+
return { ...this.circuit };
|
|
145
|
+
}
|
|
146
|
+
/** Test hook: clear cache and circuit. */
|
|
147
|
+
reset() {
|
|
148
|
+
this.cache.clear();
|
|
149
|
+
this.circuit = { consecutiveFailures: 0, openUntil: 0 };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=PromptBuildRecall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PromptBuildRecall.js","sourceRoot":"","sources":["../../src/core/PromptBuildRecall.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* selectIntelligenceProvider — choose the shared LLM provider with subscription-by-default
|
|
3
|
+
* safety guarantees.
|
|
4
|
+
*
|
|
5
|
+
* Rules:
|
|
6
|
+
* 1. Anthropic API mode requires BOTH `intelligenceProvider: "anthropic-api"` AND
|
|
7
|
+
* `intelligenceProviderConfirmed: true` in config. Either one alone is rejected with
|
|
8
|
+
* a visible warning, and selection falls through to Claude CLI.
|
|
9
|
+
* 2. With both flags set and an `ANTHROPIC_API_KEY` available, API mode engages and the
|
|
10
|
+
* caller is expected to render a billing banner (signaled via `apiModeActive`).
|
|
11
|
+
* 3. If API mode is rejected (or never requested), Claude CLI is attempted next.
|
|
12
|
+
* 4. If neither succeeds, the result is `provider: null, source: 'none'`. The caller
|
|
13
|
+
* degrades gracefully — there is NO silent fallback to the API even if an
|
|
14
|
+
* `ANTHROPIC_API_KEY` is present in the environment. The presence of the env key
|
|
15
|
+
* without explicit opt-in is reported via `apiKeyIgnored: true`.
|
|
16
|
+
*
|
|
17
|
+
* This is the single chokepoint where Instar's subscription-by-default principle is
|
|
18
|
+
* enforced. Every other LLM-gated feature shares the provider this function returns.
|
|
19
|
+
*/
|
|
20
|
+
import type { IntelligenceProvider } from './types.js';
|
|
21
|
+
export interface IntelligenceSelectionInput {
|
|
22
|
+
intelligenceProvider?: string;
|
|
23
|
+
intelligenceProviderConfirmed?: boolean;
|
|
24
|
+
anthropicApiKey?: string | undefined;
|
|
25
|
+
claudePath?: string | undefined;
|
|
26
|
+
buildClaude: (claudePath: string) => IntelligenceProvider | null;
|
|
27
|
+
buildAnthropic: (apiKey: string) => IntelligenceProvider | null;
|
|
28
|
+
}
|
|
29
|
+
export type IntelligenceSource = 'anthropic-api-confirmed' | 'claude-cli' | 'none';
|
|
30
|
+
export interface IntelligenceSelection {
|
|
31
|
+
provider: IntelligenceProvider | null;
|
|
32
|
+
source: IntelligenceSource;
|
|
33
|
+
/** Human-readable warnings the caller should surface via console + degradation log. */
|
|
34
|
+
warnings: string[];
|
|
35
|
+
/** True iff API mode is the active, confirmed selection (caller should render billing banner). */
|
|
36
|
+
apiModeActive: boolean;
|
|
37
|
+
/** True iff ANTHROPIC_API_KEY was present in env but NOT used due to subscription-by-default. */
|
|
38
|
+
apiKeyIgnored: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function selectIntelligenceProvider(input: IntelligenceSelectionInput): IntelligenceSelection;
|
|
41
|
+
//# sourceMappingURL=selectIntelligenceProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectIntelligenceProvider.d.ts","sourceRoot":"","sources":["../../src/core/selectIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,oBAAoB,GAAG,IAAI,CAAC;IACjE,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,oBAAoB,GAAG,IAAI,CAAC;CACjE;AAED,MAAM,MAAM,kBAAkB,GAC1B,yBAAyB,GACzB,YAAY,GACZ,MAAM,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,uFAAuF;IACvF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kGAAkG;IAClG,aAAa,EAAE,OAAO,CAAC;IACvB,iGAAiG;IACjG,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,0BAA0B,GAAG,qBAAqB,CAsEnG"}
|