@wrongstack/core 0.300.0 → 0.301.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chronicle/index.d.ts +2 -0
- package/dist/chronicle/index.js +297 -10
- package/dist/chronicle/project-server.js +253 -15
- package/dist/chronicle/sqlite-compaction.d.ts +20 -0
- package/dist/chronicle/sqlite-journal.d.ts +57 -0
- package/dist/coordination/agents/capability-manifest.d.ts +7 -0
- package/dist/coordination/agents/index.d.ts +3 -2
- package/dist/coordination/agents/index.js +818 -570
- package/dist/coordination/agents/project-agent-identity.d.ts +6 -6
- package/dist/coordination/agents/role-skills.d.ts +1 -1
- package/dist/coordination/agents/types.d.ts +6 -0
- package/dist/coordination/director.d.ts +2 -2
- package/dist/coordination/index.js +6778 -6438
- package/dist/coordination/mail-tools.d.ts +3 -3
- package/dist/coordination/mailbox-project-server.js +3 -4
- package/dist/coordination/mailbox-types.d.ts +6 -0
- package/dist/core/agent-response.d.ts +5 -1
- package/dist/core/agent-tools.d.ts +3 -0
- package/dist/core/context.d.ts +20 -0
- package/dist/core/index.d.ts +8 -7
- package/dist/core/index.js +893 -284
- package/dist/core/instruction-template.d.ts +6 -3
- package/dist/core/next-steps-slot.d.ts +88 -0
- package/dist/core/system-prompt-builder.d.ts +5 -3
- package/dist/core/system-prompt-memory-skills.d.ts +1 -0
- package/dist/core/system-prompt-skill-bodies.d.ts +3 -3
- package/dist/defaults/index.js +9796 -8897
- package/dist/design/index.js +11 -3
- package/dist/execution/auto-compaction-middleware.d.ts +17 -0
- package/dist/execution/autonomy-brain.d.ts +8 -2
- package/dist/execution/brain-runtime.d.ts +3 -1
- package/dist/execution/compaction-core.d.ts +41 -2
- package/dist/execution/council-brain.d.ts +37 -2
- package/dist/execution/council-orchestrator.d.ts +2 -2
- package/dist/execution/index.d.ts +11 -10
- package/dist/execution/index.js +5178 -4077
- package/dist/execution/tool-executor.d.ts +4 -1
- package/dist/execution/topic-shift-advisor.d.ts +53 -0
- package/dist/extension/index.js +8 -2
- package/dist/extension/registry.d.ts +8 -1
- package/dist/goal/index.js +11 -3
- package/dist/hooks/index.js +42 -9
- package/dist/hooks/runner.d.ts +12 -1
- package/dist/hq/index.js +41 -17
- package/dist/hq/publisher.d.ts +21 -3
- package/dist/index.d.ts +21 -20
- package/dist/index.js +10887 -8053
- package/dist/infrastructure/index.js +108 -21
- package/dist/kernel/events/session-events.d.ts +13 -0
- package/dist/kernel/events/tool-events.d.ts +3 -3
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +17 -0
- package/dist/models/provider-credentials.d.ts +54 -0
- package/dist/plugin/index.d.ts +1 -0
- package/dist/plugin/index.js +2024 -502
- package/dist/plugins/auto-review-plugin.d.ts +3 -0
- package/dist/plugins/cloud-config-sync-plugin.d.ts +22 -0
- package/dist/plugins/review-claim-registry.d.ts +23 -8
- package/dist/plugins/review-types.d.ts +2 -0
- package/dist/registry/index.js +109 -74
- package/dist/registry/tool-registry.d.ts +15 -0
- package/dist/security/capabilities.d.ts +2 -0
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +9 -2
- package/dist/security/readonly-permission-policy.d.ts +20 -0
- package/dist/session-registry-atomic-file.d.ts +32 -5
- package/dist/skills/frontmatter.d.ts +6 -0
- package/dist/skills/index.js +22 -5
- package/dist/storage/cloud-config-sync/sanitize.d.ts +54 -0
- package/dist/storage/cloud-config-sync.d.ts +87 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +854 -66
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +511 -234
- package/dist/tools/one-shot-llm-tool.d.ts +1 -0
- package/dist/tools/plugin-manager.d.ts +27 -0
- package/dist/types/config/mcp-features.d.ts +18 -11
- package/dist/types/config/root.d.ts +8 -1
- package/dist/types/config/runtime.d.ts +20 -6
- package/dist/types/config/skills-fleet-brain.d.ts +12 -4
- package/dist/types/config/tools.d.ts +16 -0
- package/dist/types/context-window.d.ts +1 -0
- package/dist/types/hooks.d.ts +14 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +1 -0
- package/dist/types/multi-agent.d.ts +2 -0
- package/dist/types/one-shot-llm.d.ts +16 -0
- package/dist/types/provider.d.ts +16 -0
- package/dist/types/runtime-capability-manifest.d.ts +168 -0
- package/dist/types/skill.d.ts +5 -0
- package/dist/types/system-prompt.d.ts +3 -1
- package/dist/types/tool-executor.d.ts +8 -1
- package/dist/utils/context-breakdown.d.ts +8 -2
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +141 -31
- package/dist/utils/regex-guard.d.ts +16 -2
- package/dist/utils/sage-output-block.d.ts +7 -10
- package/dist/utils/wstack-paths.d.ts +11 -3
- package/instructions/agents/browser.md +1 -4
- package/instructions/agents/e2e.md +17 -15
- package/instructions/agents/ios.md +3 -3
- package/instructions/agents/search.md +1 -1
- package/instructions/autonomy/goal-preamble.md +4 -4
- package/instructions/coordination/director-preamble.md +2 -3
- package/instructions/coordination/subagent-baseline.md +3 -1
- package/instructions/leader-after-task.md +12 -0
- package/instructions/llm/chimera-review.md +1 -1
- package/instructions/modes/audit-lite.md +1 -1
- package/instructions/sections/tool/common-patterns.md +18 -2
- package/instructions/sections/tool/mailbox-compact.md +1 -1
- package/instructions/sections/tool/mailbox-full.md +1 -1
- package/instructions/system-lite.md +10 -0
- package/instructions/system-pro.md +23 -14
- package/instructions/system.md +22 -13
- package/package.json +3 -3
- package/skills/api-design/SKILL.md +3 -0
- package/skills/audit-log/SKILL.md +2 -0
- package/skills/auto-review/SKILL.md +6 -1
- package/skills/bug-hunter/SKILL.md +4 -1
- package/skills/chimera/SKILL.md +3 -0
- package/skills/data-governance/SKILL.md +3 -0
- package/skills/design-system/SKILL.md +5 -2
- package/skills/docker-deploy/SKILL.md +2 -0
- package/skills/git-flow/SKILL.md +2 -0
- package/skills/mailbox-bridge/SKILL.md +3 -0
- package/skills/mnemosyne/SKILL.md +2 -0
- package/skills/multi-agent/SKILL.md +4 -1
- package/skills/node-modern/SKILL.md +4 -1
- package/skills/observability/SKILL.md +3 -0
- package/skills/output-standards/SKILL.md +2 -0
- package/skills/plugin-author/SKILL.md +4 -1
- package/skills/prompt-engineering/SKILL.md +3 -1
- package/skills/react-modern/SKILL.md +6 -3
- package/skills/refactor-planner/SKILL.md +2 -0
- package/skills/research-web/SKILL.md +3 -0
- package/skills/sdd/SKILL.md +3 -1
- package/skills/security-scanner/SKILL.md +3 -0
- package/skills/skill-creator/SKILL.md +2 -0
- package/skills/tech-stack/SKILL.md +3 -0
- package/skills/testing/SKILL.md +4 -1
- package/skills/typescript-strict/SKILL.md +4 -1
- package/skills/wrongstack-kanban/SKILL.md +6 -3
- package/skills/wrongstack-mailbox/SKILL.md +4 -1
- package/skills/wrongstack-mailbox-mcp/SKILL.md +10 -8
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/chronicle/project-server.ts
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
5
|
-
import * as
|
|
5
|
+
import * as fs10 from "node:fs";
|
|
6
6
|
import * as fsp4 from "node:fs/promises";
|
|
7
7
|
import * as net from "node:net";
|
|
8
8
|
import * as path15 from "node:path";
|
|
@@ -3696,6 +3696,7 @@ function ensureChronicleProjectServerSocketDirectory(endpoint2) {
|
|
|
3696
3696
|
|
|
3697
3697
|
// src/chronicle/sqlite-journal.ts
|
|
3698
3698
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
3699
|
+
import * as fs9 from "node:fs";
|
|
3699
3700
|
import { createRequire as createRequire2 } from "node:module";
|
|
3700
3701
|
import * as path14 from "node:path";
|
|
3701
3702
|
|
|
@@ -3941,6 +3942,25 @@ var CHRONICLE_SQLITE_FILE = "chronicle.sqlite";
|
|
|
3941
3942
|
var LEGACY_JSONL_MIGRATION_KEY = "legacy-jsonl-v1";
|
|
3942
3943
|
var LEGACY_JSONL_QUARANTINE_KEY = "legacy-jsonl-v1:quarantine";
|
|
3943
3944
|
var SCHEMA_VERSION2 = 1;
|
|
3945
|
+
var SQLITE_FIXED_OVERHEAD_BYTES = 16 * 1024 * 1024;
|
|
3946
|
+
var MIN_SQLITE_PAGE_BUDGET_BYTES = 64 * 1024;
|
|
3947
|
+
var MAX_ROLLBACK_JOURNAL_RESERVE_BYTES = 64 * 1024 * 1024;
|
|
3948
|
+
var ChronicleStorageQuotaError = class extends Error {
|
|
3949
|
+
currentBytes;
|
|
3950
|
+
batchBytes;
|
|
3951
|
+
maxBytes;
|
|
3952
|
+
path;
|
|
3953
|
+
constructor(details) {
|
|
3954
|
+
super(
|
|
3955
|
+
`Chronicle SQLite quota exceeded at ${details.path}: ${details.currentBytes} live bytes + ${details.batchBytes} batch bytes exceeds ${details.maxBytes}; lower chronicle retentionDays/maxEvents to shed data (run chronicle compact to return the freed pages to the filesystem)`
|
|
3956
|
+
);
|
|
3957
|
+
this.name = "ChronicleStorageQuotaError";
|
|
3958
|
+
this.currentBytes = details.currentBytes;
|
|
3959
|
+
this.batchBytes = details.batchBytes;
|
|
3960
|
+
this.maxBytes = details.maxBytes;
|
|
3961
|
+
this.path = details.path;
|
|
3962
|
+
}
|
|
3963
|
+
};
|
|
3944
3964
|
var Ctor2;
|
|
3945
3965
|
function loadDatabaseSync2() {
|
|
3946
3966
|
if (Ctor2) return Ctor2;
|
|
@@ -3963,6 +3983,14 @@ var ChronicleSqliteJournal = class {
|
|
|
3963
3983
|
now;
|
|
3964
3984
|
monotonicNow;
|
|
3965
3985
|
idFactory;
|
|
3986
|
+
retentionDays;
|
|
3987
|
+
maxEvents;
|
|
3988
|
+
maxBytes;
|
|
3989
|
+
retentionCheckIntervalMs;
|
|
3990
|
+
nextRetentionCheckAt = 0;
|
|
3991
|
+
retainedEventCount = 0;
|
|
3992
|
+
/** Resolved lazily by `pageSizeBytes()`; constant for an open database. */
|
|
3993
|
+
cachedPageSize;
|
|
3966
3994
|
/**
|
|
3967
3995
|
* Cached chain head per day. Cleared wholesale on any write failure so the
|
|
3968
3996
|
* next append rebuilds from the database rather than trusting a counter that
|
|
@@ -3984,10 +4012,36 @@ var ChronicleSqliteJournal = class {
|
|
|
3984
4012
|
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
3985
4013
|
this.monotonicNow = options.monotonicNow ?? (() => process.hrtime.bigint());
|
|
3986
4014
|
this.idFactory = options.idFactory ?? (() => randomUUID3());
|
|
4015
|
+
if (options.retentionDays !== void 0 && (!Number.isFinite(options.retentionDays) || options.retentionDays <= 0)) {
|
|
4016
|
+
throw new RangeError("retentionDays must be a positive finite number");
|
|
4017
|
+
}
|
|
4018
|
+
if (options.retentionCheckIntervalMs !== void 0 && (!Number.isFinite(options.retentionCheckIntervalMs) || options.retentionCheckIntervalMs <= 0)) {
|
|
4019
|
+
throw new RangeError("retentionCheckIntervalMs must be a positive finite number");
|
|
4020
|
+
}
|
|
4021
|
+
if (options.maxEvents !== void 0 && (!Number.isInteger(options.maxEvents) || options.maxEvents < 1)) {
|
|
4022
|
+
throw new RangeError("maxEvents must be a positive integer");
|
|
4023
|
+
}
|
|
4024
|
+
if (options.maxBytes !== void 0 && (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 1)) {
|
|
4025
|
+
throw new RangeError("maxBytes must be a positive safe integer");
|
|
4026
|
+
}
|
|
4027
|
+
const minimumQuotaBytes = SQLITE_FIXED_OVERHEAD_BYTES + 2 * MIN_SQLITE_PAGE_BUDGET_BYTES;
|
|
4028
|
+
if (options.maxBytes !== void 0 && options.maxBytes < minimumQuotaBytes) {
|
|
4029
|
+
throw new RangeError(`maxBytes must be at least ${minimumQuotaBytes}`);
|
|
4030
|
+
}
|
|
4031
|
+
this.retentionDays = options.retentionDays;
|
|
4032
|
+
this.maxEvents = options.maxEvents;
|
|
4033
|
+
this.maxBytes = options.maxBytes;
|
|
4034
|
+
this.retentionCheckIntervalMs = options.retentionCheckIntervalMs ?? 60 * 60 * 1e3;
|
|
3987
4035
|
const Database = loadDatabaseSync2();
|
|
3988
4036
|
this.db = new Database(this.dbPath);
|
|
3989
|
-
this.db.exec(
|
|
4037
|
+
this.db.exec(`PRAGMA journal_mode = ${this.maxBytes === void 0 ? "WAL" : "DELETE"}`);
|
|
3990
4038
|
this.ensureSchema();
|
|
4039
|
+
this.configureByteQuota();
|
|
4040
|
+
if (this.maxEvents !== void 0) {
|
|
4041
|
+
const row = this.db.prepare("SELECT COUNT(*) AS count FROM events").get();
|
|
4042
|
+
this.retainedEventCount = row.count;
|
|
4043
|
+
this.enforceEventLimitAtStartup();
|
|
4044
|
+
}
|
|
3991
4045
|
}
|
|
3992
4046
|
close() {
|
|
3993
4047
|
this.db.close();
|
|
@@ -4046,8 +4100,10 @@ var ChronicleSqliteJournal = class {
|
|
|
4046
4100
|
events.push(event);
|
|
4047
4101
|
previous = { sequence: event.sequence, hash: event.hash };
|
|
4048
4102
|
}
|
|
4103
|
+
let retainedCountAfterCommit = this.retainedEventCount;
|
|
4049
4104
|
try {
|
|
4050
4105
|
this.db.exec("BEGIN IMMEDIATE");
|
|
4106
|
+
this.assertWithinByteQuota(events);
|
|
4051
4107
|
const insert = this.db.prepare(
|
|
4052
4108
|
`INSERT INTO events (
|
|
4053
4109
|
day, sequence, event_id, hash, previous_hash, occurred_at, event_type, outcome,
|
|
@@ -4079,6 +4135,8 @@ var ChronicleSqliteJournal = class {
|
|
|
4079
4135
|
JSON.stringify(event)
|
|
4080
4136
|
);
|
|
4081
4137
|
}
|
|
4138
|
+
retainedCountAfterCommit = this.enforceEventLimitWithinTransaction();
|
|
4139
|
+
this.assertActualAllocationWithinQuota();
|
|
4082
4140
|
this.db.exec("COMMIT");
|
|
4083
4141
|
} catch (error) {
|
|
4084
4142
|
try {
|
|
@@ -4088,11 +4146,13 @@ var ChronicleSqliteJournal = class {
|
|
|
4088
4146
|
this.anchors.clear();
|
|
4089
4147
|
this.counters.failedEvents += inputs.length;
|
|
4090
4148
|
this.lastBatchDurationMs = performance.now() - started;
|
|
4091
|
-
throw error;
|
|
4149
|
+
throw this.normalizeQuotaError(error);
|
|
4092
4150
|
}
|
|
4093
4151
|
this.anchors.set(day, previous);
|
|
4094
4152
|
this.counters.persistedEvents += events.length;
|
|
4153
|
+
this.retainedEventCount = retainedCountAfterCommit;
|
|
4095
4154
|
this.lastBatchDurationMs = performance.now() - started;
|
|
4155
|
+
await this.enforceRetentionIfDue();
|
|
4096
4156
|
return events;
|
|
4097
4157
|
}
|
|
4098
4158
|
async readAll() {
|
|
@@ -4153,6 +4213,145 @@ var ChronicleSqliteJournal = class {
|
|
|
4153
4213
|
}
|
|
4154
4214
|
return { ok: true, entries, lastSequence, lastHash };
|
|
4155
4215
|
}
|
|
4216
|
+
async enforceRetentionIfDue() {
|
|
4217
|
+
if (this.retentionDays === void 0) return;
|
|
4218
|
+
const now = this.now();
|
|
4219
|
+
if (now.getTime() < this.nextRetentionCheckAt) return;
|
|
4220
|
+
this.nextRetentionCheckAt = now.getTime() + this.retentionCheckIntervalMs;
|
|
4221
|
+
try {
|
|
4222
|
+
await this.purge({ retentionDays: this.retentionDays });
|
|
4223
|
+
} catch {
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
configureByteQuota() {
|
|
4227
|
+
if (this.maxBytes === void 0) {
|
|
4228
|
+
this.db.exec("PRAGMA max_page_count = 2147483646");
|
|
4229
|
+
return;
|
|
4230
|
+
}
|
|
4231
|
+
const halfSplit = Math.floor((this.maxBytes - SQLITE_FIXED_OVERHEAD_BYTES) / 2);
|
|
4232
|
+
const journalReserve = Math.min(MAX_ROLLBACK_JOURNAL_RESERVE_BYTES, halfSplit);
|
|
4233
|
+
const mainBudget = this.maxBytes - SQLITE_FIXED_OVERHEAD_BYTES - journalReserve;
|
|
4234
|
+
if (mainBudget < MIN_SQLITE_PAGE_BUDGET_BYTES) {
|
|
4235
|
+
throw new Error(
|
|
4236
|
+
`maxBytes must be at least ${SQLITE_FIXED_OVERHEAD_BYTES + 2 * MIN_SQLITE_PAGE_BUDGET_BYTES}`
|
|
4237
|
+
);
|
|
4238
|
+
}
|
|
4239
|
+
const maxPages = Math.max(1, Math.floor(mainBudget / this.pageSizeBytes()));
|
|
4240
|
+
this.db.exec(`PRAGMA max_page_count = ${maxPages}`);
|
|
4241
|
+
}
|
|
4242
|
+
/**
|
|
4243
|
+
* Bytes the journal actually occupies: live pages in the main database plus
|
|
4244
|
+
* whatever the rollback/WAL sidecars currently hold.
|
|
4245
|
+
*
|
|
4246
|
+
* The quota MUST be measured this way rather than by `statSync` on the main
|
|
4247
|
+
* database file. SQLite never returns freed pages to the filesystem — it
|
|
4248
|
+
* parks them on the freelist and reuses them — so a database that once grew
|
|
4249
|
+
* large keeps that size forever, even after retention and `maxEvents` have
|
|
4250
|
+
* evicted almost everything. Measuring the file instead wedged the journal
|
|
4251
|
+
* permanently: a 3.9 GB file whose live data was 243 MB failed a 512 MB
|
|
4252
|
+
* quota on EVERY append, and the only escape (`chronicle compact`) refuses
|
|
4253
|
+
* to run while the daemon is up — and the daemon respawns on demand. Live
|
|
4254
|
+
* pages shrink when retention deletes rows, so the quota can recover on its
|
|
4255
|
+
* own; allocated size cannot.
|
|
4256
|
+
*
|
|
4257
|
+
* The sidecars keep their on-disk size: they are transient and genuinely
|
|
4258
|
+
* bounded by `configureByteQuota`'s half-of-budget split.
|
|
4259
|
+
*/
|
|
4260
|
+
aggregateLiveBytes() {
|
|
4261
|
+
let total = 0;
|
|
4262
|
+
for (const file of [`${this.dbPath}-journal`, `${this.dbPath}-wal`, `${this.dbPath}-shm`]) {
|
|
4263
|
+
try {
|
|
4264
|
+
total += fs9.statSync(file).size;
|
|
4265
|
+
} catch (error) {
|
|
4266
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
return total + this.mainDatabaseLiveBytes();
|
|
4270
|
+
}
|
|
4271
|
+
/** Live (non-freelist) pages of the main database, in bytes. */
|
|
4272
|
+
mainDatabaseLiveBytes() {
|
|
4273
|
+
const pageCount = Number(
|
|
4274
|
+
this.db.prepare("PRAGMA page_count").get().page_count
|
|
4275
|
+
);
|
|
4276
|
+
const freelist = Number(
|
|
4277
|
+
this.db.prepare("PRAGMA freelist_count").get().freelist_count
|
|
4278
|
+
);
|
|
4279
|
+
return Math.max(0, pageCount - freelist) * this.pageSizeBytes();
|
|
4280
|
+
}
|
|
4281
|
+
/** Page size never changes for an open database; resolve it once. */
|
|
4282
|
+
pageSizeBytes() {
|
|
4283
|
+
if (this.cachedPageSize === void 0) {
|
|
4284
|
+
this.cachedPageSize = Number(
|
|
4285
|
+
this.db.prepare("PRAGMA page_size").get().page_size
|
|
4286
|
+
);
|
|
4287
|
+
}
|
|
4288
|
+
return this.cachedPageSize;
|
|
4289
|
+
}
|
|
4290
|
+
normalizeQuotaError(error) {
|
|
4291
|
+
if (this.maxBytes === void 0 || !(error instanceof Error)) return error;
|
|
4292
|
+
const sqliteError = error;
|
|
4293
|
+
const quotaExhausted = sqliteError.code === "SQLITE_FULL" || sqliteError.code === 13 || sqliteError.errcode === 13 || /database or disk is full/i.test(sqliteError.message);
|
|
4294
|
+
if (!quotaExhausted) return error;
|
|
4295
|
+
return new ChronicleStorageQuotaError({
|
|
4296
|
+
currentBytes: this.aggregateLiveBytes(),
|
|
4297
|
+
batchBytes: 0,
|
|
4298
|
+
maxBytes: this.maxBytes,
|
|
4299
|
+
path: this.dbPath
|
|
4300
|
+
});
|
|
4301
|
+
}
|
|
4302
|
+
assertActualAllocationWithinQuota() {
|
|
4303
|
+
if (this.maxBytes === void 0) return;
|
|
4304
|
+
const currentBytes = this.aggregateLiveBytes();
|
|
4305
|
+
if (currentBytes <= this.maxBytes) return;
|
|
4306
|
+
throw new ChronicleStorageQuotaError({
|
|
4307
|
+
currentBytes,
|
|
4308
|
+
batchBytes: 0,
|
|
4309
|
+
maxBytes: this.maxBytes,
|
|
4310
|
+
path: this.dbPath
|
|
4311
|
+
});
|
|
4312
|
+
}
|
|
4313
|
+
assertWithinByteQuota(events) {
|
|
4314
|
+
if (this.maxBytes === void 0) return;
|
|
4315
|
+
const currentBytes = this.aggregateLiveBytes();
|
|
4316
|
+
const batchBytes = Buffer.byteLength(JSON.stringify(events), "utf8");
|
|
4317
|
+
if (currentBytes + batchBytes <= this.maxBytes) return;
|
|
4318
|
+
throw new ChronicleStorageQuotaError({
|
|
4319
|
+
currentBytes,
|
|
4320
|
+
batchBytes,
|
|
4321
|
+
maxBytes: this.maxBytes,
|
|
4322
|
+
path: this.dbPath
|
|
4323
|
+
});
|
|
4324
|
+
}
|
|
4325
|
+
enforceEventLimitWithinTransaction() {
|
|
4326
|
+
const row = this.db.prepare("SELECT COUNT(*) AS count FROM events").get();
|
|
4327
|
+
if (this.maxEvents === void 0 || row.count <= this.maxEvents) return row.count;
|
|
4328
|
+
const excess = row.count - this.maxEvents;
|
|
4329
|
+
const boundary = this.db.prepare("SELECT day, sequence, hash FROM events ORDER BY day, sequence LIMIT 1 OFFSET ?").get(excess - 1);
|
|
4330
|
+
if (!boundary) return row.count;
|
|
4331
|
+
this.db.prepare(
|
|
4332
|
+
`INSERT INTO chain_checkpoint (day, sequence, hash) VALUES (?, ?, ?)
|
|
4333
|
+
ON CONFLICT(day) DO UPDATE SET sequence = excluded.sequence, hash = excluded.hash`
|
|
4334
|
+
).run(boundary.day, boundary.sequence, boundary.hash);
|
|
4335
|
+
this.db.prepare("DELETE FROM events WHERE day < ? OR (day = ? AND sequence <= ?)").run(boundary.day, boundary.day, boundary.sequence);
|
|
4336
|
+
this.db.prepare("DELETE FROM chain_checkpoint WHERE day < ?").run(boundary.day);
|
|
4337
|
+
this.anchors.clear();
|
|
4338
|
+
return this.maxEvents;
|
|
4339
|
+
}
|
|
4340
|
+
enforceEventLimitAtStartup() {
|
|
4341
|
+
if (this.maxEvents === void 0 || this.retainedEventCount <= this.maxEvents) return;
|
|
4342
|
+
try {
|
|
4343
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
4344
|
+
const retainedCount = this.enforceEventLimitWithinTransaction();
|
|
4345
|
+
this.db.exec("COMMIT");
|
|
4346
|
+
this.retainedEventCount = retainedCount;
|
|
4347
|
+
} catch (error) {
|
|
4348
|
+
try {
|
|
4349
|
+
this.db.exec("ROLLBACK");
|
|
4350
|
+
} catch {
|
|
4351
|
+
}
|
|
4352
|
+
throw error;
|
|
4353
|
+
}
|
|
4354
|
+
}
|
|
4156
4355
|
/**
|
|
4157
4356
|
* Drop events older than the retention window.
|
|
4158
4357
|
*
|
|
@@ -4198,6 +4397,7 @@ var ChronicleSqliteJournal = class {
|
|
|
4198
4397
|
};
|
|
4199
4398
|
}
|
|
4200
4399
|
this.anchors.clear();
|
|
4400
|
+
this.retainedEventCount = Math.max(0, this.retainedEventCount - count);
|
|
4201
4401
|
return { ...empty, deletedCount: count };
|
|
4202
4402
|
}
|
|
4203
4403
|
/**
|
|
@@ -4229,7 +4429,9 @@ var ChronicleSqliteJournal = class {
|
|
|
4229
4429
|
ON CONFLICT(day) DO UPDATE SET sequence = excluded.sequence, hash = excluded.hash`
|
|
4230
4430
|
);
|
|
4231
4431
|
this.db.exec("BEGIN IMMEDIATE");
|
|
4432
|
+
let retainedCountAfterCommit = this.retainedEventCount;
|
|
4232
4433
|
try {
|
|
4434
|
+
this.assertWithinByteQuota([]);
|
|
4233
4435
|
await load({
|
|
4234
4436
|
insert: (day, event) => {
|
|
4235
4437
|
const row = projectEvent(event);
|
|
@@ -4259,16 +4461,19 @@ var ChronicleSqliteJournal = class {
|
|
|
4259
4461
|
checkpoint.run(day, sequence, hash);
|
|
4260
4462
|
}
|
|
4261
4463
|
});
|
|
4464
|
+
retainedCountAfterCommit = this.enforceEventLimitWithinTransaction();
|
|
4465
|
+
this.assertActualAllocationWithinQuota();
|
|
4262
4466
|
this.db.exec("COMMIT");
|
|
4263
4467
|
} catch (error) {
|
|
4264
4468
|
try {
|
|
4265
4469
|
this.db.exec("ROLLBACK");
|
|
4266
4470
|
} catch {
|
|
4267
4471
|
}
|
|
4268
|
-
throw error;
|
|
4472
|
+
throw this.normalizeQuotaError(error);
|
|
4269
4473
|
} finally {
|
|
4270
4474
|
this.anchors.clear();
|
|
4271
4475
|
}
|
|
4476
|
+
this.retainedEventCount = retainedCountAfterCommit;
|
|
4272
4477
|
}
|
|
4273
4478
|
/**
|
|
4274
4479
|
* A read engine over this journal's own connection.
|
|
@@ -4451,7 +4656,7 @@ function parseArgs(argv) {
|
|
|
4451
4656
|
projectId: values.get("--project-id"),
|
|
4452
4657
|
projectDir: path15.resolve(values.get("--project-dir")),
|
|
4453
4658
|
workspaceId: values.get("--workspace-id"),
|
|
4454
|
-
retentionDays: Number.isFinite(retentionInput) && retentionInput
|
|
4659
|
+
retentionDays: Number.isFinite(retentionInput) && retentionInput > 0 ? retentionInput : 30
|
|
4455
4660
|
};
|
|
4456
4661
|
}
|
|
4457
4662
|
useDaemonPerfDefaults();
|
|
@@ -4524,7 +4729,16 @@ var quarantinedFamilies = [];
|
|
|
4524
4729
|
function store() {
|
|
4525
4730
|
sqliteStore ??= (async () => {
|
|
4526
4731
|
await fsp4.mkdir(chronicleDirectory, { recursive: true });
|
|
4527
|
-
const journal = new ChronicleSqliteJournal({
|
|
4732
|
+
const journal = new ChronicleSqliteJournal({
|
|
4733
|
+
directory: chronicleDirectory,
|
|
4734
|
+
retentionDays: parsed.retentionDays,
|
|
4735
|
+
// Bound burst growth independently of age retention. Prefix eviction keeps
|
|
4736
|
+
// at most this many rows while preserving chain verification via checkpoints.
|
|
4737
|
+
maxEvents: 1e5,
|
|
4738
|
+
// Formal aggregate allocation ceiling. Quota-enabled journals reserve
|
|
4739
|
+
// rollback-journal headroom and constrain the main database with max_page_count.
|
|
4740
|
+
maxBytes: 512 * 1024 * 1024
|
|
4741
|
+
});
|
|
4528
4742
|
try {
|
|
4529
4743
|
const result = await importLegacyChronicleJournal(journal, chronicleDirectory);
|
|
4530
4744
|
quarantinedFamilies = result.quarantined;
|
|
@@ -4734,18 +4948,32 @@ async function dispatch(op, rawArgs) {
|
|
|
4734
4948
|
}
|
|
4735
4949
|
}
|
|
4736
4950
|
}
|
|
4737
|
-
function
|
|
4738
|
-
if (state.socket.destroyed) return;
|
|
4951
|
+
function encodeResponse(state, message) {
|
|
4952
|
+
if (state.socket.destroyed) return void 0;
|
|
4739
4953
|
const encoded = encodeChronicleProjectServerMessage(message);
|
|
4740
4954
|
if (encoded.length > CHRONICLE_PROJECT_SERVER_MAX_FRAME_CHARS) {
|
|
4741
4955
|
state.socket.destroy(new Error("Chronicle project server response exceeded frame limit"));
|
|
4742
|
-
return;
|
|
4956
|
+
return void 0;
|
|
4743
4957
|
}
|
|
4744
4958
|
if (state.socket.writableLength > MAX_CLIENT_WRITE_BUFFER_BYTES) {
|
|
4745
4959
|
state.socket.destroy(new Error("Chronicle client fell too far behind on reads"));
|
|
4746
|
-
return;
|
|
4960
|
+
return void 0;
|
|
4747
4961
|
}
|
|
4748
|
-
|
|
4962
|
+
return encoded;
|
|
4963
|
+
}
|
|
4964
|
+
function send(state, message) {
|
|
4965
|
+
const encoded = encodeResponse(state, message);
|
|
4966
|
+
if (encoded !== void 0) state.socket.write(encoded);
|
|
4967
|
+
}
|
|
4968
|
+
async function sendAcknowledgement(state, message) {
|
|
4969
|
+
const encoded = encodeResponse(state, message);
|
|
4970
|
+
if (encoded === void 0) throw new Error("Chronicle client disconnected before acknowledgement");
|
|
4971
|
+
await new Promise((resolve12, reject) => {
|
|
4972
|
+
state.socket.write(encoded, (error) => {
|
|
4973
|
+
if (error) reject(error);
|
|
4974
|
+
else resolve12();
|
|
4975
|
+
});
|
|
4976
|
+
});
|
|
4749
4977
|
}
|
|
4750
4978
|
async function handleMessage(state, message) {
|
|
4751
4979
|
if (message.authToken !== authToken) {
|
|
@@ -4759,7 +4987,12 @@ async function handleMessage(state, message) {
|
|
|
4759
4987
|
return;
|
|
4760
4988
|
}
|
|
4761
4989
|
if (message.type === "shutdown") {
|
|
4762
|
-
|
|
4990
|
+
await sendAcknowledgement(state, {
|
|
4991
|
+
type: "response",
|
|
4992
|
+
id: message.id,
|
|
4993
|
+
ok: true,
|
|
4994
|
+
result: { stopped: true }
|
|
4995
|
+
});
|
|
4763
4996
|
await stop(message.reason ?? "client request");
|
|
4764
4997
|
return;
|
|
4765
4998
|
}
|
|
@@ -4829,9 +5062,14 @@ async function stop(_reason) {
|
|
|
4829
5062
|
if (idleTimer) clearTimeout(idleTimer);
|
|
4830
5063
|
idleTimer = void 0;
|
|
4831
5064
|
await removeOwnedMetadata();
|
|
4832
|
-
await new Promise((resolve12) => server.close(() => resolve12()));
|
|
4833
5065
|
for (const state of clients) state.socket.destroy();
|
|
4834
5066
|
clients.clear();
|
|
5067
|
+
await new Promise((resolve12, reject) => {
|
|
5068
|
+
server.close((error) => {
|
|
5069
|
+
if (error) reject(error);
|
|
5070
|
+
else resolve12();
|
|
5071
|
+
});
|
|
5072
|
+
});
|
|
4835
5073
|
await watcher?.close().catch(() => {
|
|
4836
5074
|
});
|
|
4837
5075
|
watcher = void 0;
|
|
@@ -4882,7 +5120,7 @@ server.on("error", (error) => {
|
|
|
4882
5120
|
probe.once("error", () => {
|
|
4883
5121
|
probe.destroy();
|
|
4884
5122
|
try {
|
|
4885
|
-
|
|
5123
|
+
fs10.rmSync(endpoint, { force: true });
|
|
4886
5124
|
} catch {
|
|
4887
5125
|
}
|
|
4888
5126
|
probingExistingEndpoint = false;
|
|
@@ -4895,7 +5133,7 @@ server.on("error", (error) => {
|
|
|
4895
5133
|
server.on("listening", () => {
|
|
4896
5134
|
if (process.platform !== "win32") {
|
|
4897
5135
|
try {
|
|
4898
|
-
|
|
5136
|
+
fs10.chmodSync(endpoint, 384);
|
|
4899
5137
|
} catch {
|
|
4900
5138
|
}
|
|
4901
5139
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ChronicleSqliteCompactionOptions {
|
|
2
|
+
dbPath: string;
|
|
3
|
+
endpointMetadataPath?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ChronicleSqliteCompactionResult {
|
|
6
|
+
beforeBytes: number;
|
|
7
|
+
afterBytes: number;
|
|
8
|
+
reclaimedBytes: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class ChronicleCompactionBusyError extends Error {
|
|
11
|
+
constructor(pid: number);
|
|
12
|
+
}
|
|
13
|
+
/** Aggregate allocated bytes for the SQLite database and its WAL/SHM sidecars. */
|
|
14
|
+
export declare function chronicleSqliteAllocatedBytes(dbPath: string): number;
|
|
15
|
+
/**
|
|
16
|
+
* Reclaim free SQLite pages. The caller must keep the Chronicle project server
|
|
17
|
+
* stopped for the entire operation; this function refuses known live servers.
|
|
18
|
+
*/
|
|
19
|
+
export declare function compactChronicleSqlite(options: ChronicleSqliteCompactionOptions): ChronicleSqliteCompactionResult;
|
|
20
|
+
//# sourceMappingURL=sqlite-compaction.d.ts.map
|
|
@@ -6,12 +6,31 @@ export declare const CHRONICLE_SQLITE_FILE = "chronicle.sqlite";
|
|
|
6
6
|
export declare const LEGACY_JSONL_MIGRATION_KEY = "legacy-jsonl-v1";
|
|
7
7
|
/** Day families the import refused to move, recorded so they are never retried silently. */
|
|
8
8
|
export declare const LEGACY_JSONL_QUARANTINE_KEY = "legacy-jsonl-v1:quarantine";
|
|
9
|
+
export declare class ChronicleStorageQuotaError extends Error {
|
|
10
|
+
readonly currentBytes: number;
|
|
11
|
+
readonly batchBytes: number;
|
|
12
|
+
readonly maxBytes: number;
|
|
13
|
+
readonly path: string;
|
|
14
|
+
constructor(details: {
|
|
15
|
+
currentBytes: number;
|
|
16
|
+
batchBytes: number;
|
|
17
|
+
maxBytes: number;
|
|
18
|
+
path: string;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
9
21
|
export interface ChronicleSqliteJournalOptions {
|
|
10
22
|
/** Directory holding the journal, i.e. `<projectDir>/chronicle`. */
|
|
11
23
|
directory: string;
|
|
12
24
|
now?: (() => Date) | undefined;
|
|
13
25
|
monotonicNow?: (() => bigint) | undefined;
|
|
14
26
|
idFactory?: (() => string) | undefined;
|
|
27
|
+
/** Optional age bound, enforced during periodic maintenance. */
|
|
28
|
+
retentionDays?: number | undefined;
|
|
29
|
+
/** Optional row ceiling; oldest rows are checkpointed and evicted after each append. */
|
|
30
|
+
maxEvents?: number | undefined;
|
|
31
|
+
/** Aggregate SQLite allocation ceiling across the database, WAL, and SHM files. */
|
|
32
|
+
maxBytes?: number | undefined;
|
|
33
|
+
retentionCheckIntervalMs?: number | undefined;
|
|
15
34
|
}
|
|
16
35
|
export interface ChronicleSqlitePurgeOptions {
|
|
17
36
|
retentionDays: number;
|
|
@@ -43,6 +62,14 @@ export declare class ChronicleSqliteJournal {
|
|
|
43
62
|
private readonly now;
|
|
44
63
|
private readonly monotonicNow;
|
|
45
64
|
private readonly idFactory;
|
|
65
|
+
private readonly retentionDays;
|
|
66
|
+
private readonly maxEvents;
|
|
67
|
+
private readonly maxBytes;
|
|
68
|
+
private readonly retentionCheckIntervalMs;
|
|
69
|
+
private nextRetentionCheckAt;
|
|
70
|
+
private retainedEventCount;
|
|
71
|
+
/** Resolved lazily by `pageSizeBytes()`; constant for an open database. */
|
|
72
|
+
private cachedPageSize;
|
|
46
73
|
/**
|
|
47
74
|
* Cached chain head per day. Cleared wholesale on any write failure so the
|
|
48
75
|
* next append rebuilds from the database rather than trusting a counter that
|
|
@@ -83,6 +110,36 @@ export declare class ChronicleSqliteJournal {
|
|
|
83
110
|
* an in-place edit that left the links intact.
|
|
84
111
|
*/
|
|
85
112
|
verify(): Promise<ChronicleVerifyResult>;
|
|
113
|
+
private enforceRetentionIfDue;
|
|
114
|
+
private configureByteQuota;
|
|
115
|
+
/**
|
|
116
|
+
* Bytes the journal actually occupies: live pages in the main database plus
|
|
117
|
+
* whatever the rollback/WAL sidecars currently hold.
|
|
118
|
+
*
|
|
119
|
+
* The quota MUST be measured this way rather than by `statSync` on the main
|
|
120
|
+
* database file. SQLite never returns freed pages to the filesystem — it
|
|
121
|
+
* parks them on the freelist and reuses them — so a database that once grew
|
|
122
|
+
* large keeps that size forever, even after retention and `maxEvents` have
|
|
123
|
+
* evicted almost everything. Measuring the file instead wedged the journal
|
|
124
|
+
* permanently: a 3.9 GB file whose live data was 243 MB failed a 512 MB
|
|
125
|
+
* quota on EVERY append, and the only escape (`chronicle compact`) refuses
|
|
126
|
+
* to run while the daemon is up — and the daemon respawns on demand. Live
|
|
127
|
+
* pages shrink when retention deletes rows, so the quota can recover on its
|
|
128
|
+
* own; allocated size cannot.
|
|
129
|
+
*
|
|
130
|
+
* The sidecars keep their on-disk size: they are transient and genuinely
|
|
131
|
+
* bounded by `configureByteQuota`'s half-of-budget split.
|
|
132
|
+
*/
|
|
133
|
+
private aggregateLiveBytes;
|
|
134
|
+
/** Live (non-freelist) pages of the main database, in bytes. */
|
|
135
|
+
private mainDatabaseLiveBytes;
|
|
136
|
+
/** Page size never changes for an open database; resolve it once. */
|
|
137
|
+
private pageSizeBytes;
|
|
138
|
+
private normalizeQuotaError;
|
|
139
|
+
private assertActualAllocationWithinQuota;
|
|
140
|
+
private assertWithinByteQuota;
|
|
141
|
+
private enforceEventLimitWithinTransaction;
|
|
142
|
+
private enforceEventLimitAtStartup;
|
|
86
143
|
/**
|
|
87
144
|
* Drop events older than the retention window.
|
|
88
145
|
*
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility export for callers that historically loaded the runtime
|
|
3
|
+
* capability contract from the agent catalog. The contract itself lives in
|
|
4
|
+
* `types/` so low-level prompt composition does not depend on coordination.
|
|
5
|
+
*/
|
|
6
|
+
export * from '../../types/runtime-capability-manifest.js';
|
|
7
|
+
//# sourceMappingURL=capability-manifest.d.ts.map
|
|
@@ -24,13 +24,14 @@ import { META_AGENTS } from './phase9-meta.js';
|
|
|
24
24
|
import { WAVE4_AGENTS } from './phase9-wave4-platform-meta.js';
|
|
25
25
|
import { ROLE_SKILL_SETS, SHADOW_AGENT_SKILLS } from './role-skills.js';
|
|
26
26
|
import type { AgentDefinition } from './types.js';
|
|
27
|
-
export * from './
|
|
28
|
-
export * from './types.js';
|
|
27
|
+
export * from './capability-manifest.js';
|
|
29
28
|
export * from './phase3-wave1-platform.js';
|
|
30
29
|
export * from './phase3-wave2-meta.js';
|
|
31
30
|
export * from './phase8-wave3-products.js';
|
|
32
31
|
export * from './phase9-wave4-platform-meta.js';
|
|
33
32
|
export * from './project-agent-identity.js';
|
|
33
|
+
export * from './role-skills.js';
|
|
34
|
+
export * from './types.js';
|
|
34
35
|
export { BUILD_AGENTS, DELIVERY_AGENTS, DISCOVERY_AGENTS, DOMAIN_AGENTS, KNOWLEDGE_AGENTS, META_AGENTS, PLANNING_AGENTS, REVIEW_AGENTS, VERIFY_AGENTS, WAVE1_AGENTS, WAVE2_AGENTS, WAVE3_AGENTS, WAVE4_AGENTS, };
|
|
35
36
|
/** Every catalog agent, in phase order, enriched with its curated skills. */
|
|
36
37
|
export declare const ALL_AGENT_DEFINITIONS: AgentDefinition[];
|