@plurnk/plurnk-service 1.0.4 → 1.0.6
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/.env.defaults +55 -31
- package/INSTALL.md +3 -3
- package/README.md +4 -4
- package/SPEC.md +205 -193
- package/dist/Paths.js +1 -1
- package/dist/Paths.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts +18 -18
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +17 -17
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +11 -11
- package/dist/core/Dispatcher.d.ts +11 -16
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +122 -125
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +20 -23
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +213 -210
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +108 -130
- package/dist/core/PacketBuilder.d.ts +10 -11
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +204 -128
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts +8 -8
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +10 -10
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts +1 -0
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +9 -3
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +4 -1
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +18 -9
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/TelemetryChannel.d.ts +5 -5
- package/dist/core/TelemetryChannel.d.ts.map +1 -1
- package/dist/core/TelemetryChannel.js +9 -9
- package/dist/core/TelemetryChannel.js.map +1 -1
- package/dist/core/caps/CapsResolve.js +2 -2
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +2 -2
- package/dist/core/caps/DbNotifyCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +7 -7
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +2 -2
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +4 -4
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/content-hash.d.ts +2 -0
- package/dist/core/content-hash.d.ts.map +1 -0
- package/dist/core/content-hash.js +6 -0
- package/dist/core/content-hash.js.map +1 -0
- package/dist/core/fork.d.ts +1 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +31 -26
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +41 -32
- package/dist/core/git-env.d.ts.map +1 -1
- package/dist/core/git-env.js +23 -7
- package/dist/core/git-env.js.map +1 -1
- package/dist/core/git-iso.d.ts +9 -0
- package/dist/core/git-iso.d.ts.map +1 -0
- package/dist/core/git-iso.js +227 -0
- package/dist/core/git-iso.js.map +1 -0
- package/dist/core/git-membership.d.ts +2 -2
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +70 -50
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts +1 -1
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +21 -11
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +81 -22
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +2 -2
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +19 -12
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/run-ops.sql +26 -26
- package/dist/core/scheme-types.d.ts +6 -11
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/search-gate.js +0 -0
- package/dist/core/search-gate.js.map +1 -1
- package/dist/core/token-ruler.d.ts +2 -0
- package/dist/core/token-ruler.d.ts.map +1 -0
- package/dist/core/token-ruler.js +18 -0
- package/dist/core/token-ruler.js.map +1 -0
- package/dist/core/worker-cap.d.ts +8 -0
- package/dist/core/worker-cap.d.ts.map +1 -0
- package/dist/core/worker-cap.js +20 -0
- package/dist/core/worker-cap.js.map +1 -0
- package/dist/core/{session-settings.d.ts → workspace-settings.d.ts} +5 -5
- package/dist/core/workspace-settings.d.ts.map +1 -0
- package/dist/core/{session-settings.js → workspace-settings.js} +13 -13
- package/dist/core/workspace-settings.js.map +1 -0
- package/dist/digest/Digest.d.ts +3 -3
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +79 -73
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +18 -18
- package/dist/schemes/Exec.d.ts +9 -2
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +121 -68
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +3 -3
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.js +23 -23
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Known.js +5 -5
- package/dist/schemes/Known.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +63 -14
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +49 -8
- package/dist/schemes/Plurnk.js +5 -5
- package/dist/schemes/Plurnk.js.map +1 -1
- package/dist/schemes/Skill.js +5 -5
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Unknown.js +5 -5
- package/dist/schemes/Unknown.js.map +1 -1
- package/dist/schemes/{Run.d.ts → Worker.d.ts} +2 -2
- package/dist/schemes/Worker.d.ts.map +1 -0
- package/dist/schemes/{Run.js → Worker.js} +67 -67
- package/dist/schemes/Worker.js.map +1 -0
- package/dist/schemes/_entry-crud.js +11 -11
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +29 -29
- package/dist/schemes/_entry-find.d.ts +2 -1
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +31 -19
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +9 -9
- package/dist/schemes/_entry-graph.d.ts +2 -2
- package/dist/schemes/_entry-graph.d.ts.map +1 -1
- package/dist/schemes/_entry-graph.js +18 -18
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-graph.sql +8 -8
- package/dist/schemes/_entry-manifest.d.ts +1 -1
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +74 -50
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +4 -4
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +25 -25
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +9 -9
- package/dist/schemes/_entry-semantic.d.ts +2 -1
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +23 -4
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/_entry-semantic.sql +18 -4
- package/dist/schemes/_entry-send.d.ts +1 -1
- package/dist/schemes/_entry-send.d.ts.map +1 -1
- package/dist/schemes/_entry-send.js +6 -6
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/exec-abort.js +1 -1
- package/dist/schemes/exec-abort.js.map +1 -1
- package/dist/server/Daemon.d.ts +50 -48
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +307 -297
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/client-input.js +20 -20
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +7 -7
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +27 -27
- package/dist/server/envelope.d.ts +20 -20
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +86 -86
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +39 -39
- package/dist/server/logEntry.d.ts +1 -1
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +1 -1
- package/dist/server/logEntry.js.map +1 -1
- package/dist/server/loop-model.d.ts +3 -0
- package/dist/server/loop-model.d.ts.map +1 -0
- package/dist/server/loop-model.js +26 -0
- package/dist/server/loop-model.js.map +1 -0
- package/dist/server/loopDocs.d.ts +1 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +6 -6
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/seam-entry-read.sql +2 -2
- package/dist/server/seam-log-read.sql +3 -3
- package/dist/server/seam-loop.sql +3 -3
- package/dist/server/seam-proposal-list.sql +4 -4
- package/dist/server/yolo.js +2 -2
- package/dist/server/yolo.js.map +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +21 -6
- package/dist/service.js.map +1 -1
- package/migrations/0000-00-00.01_schema.sql +84 -85
- package/package.json +17 -14
- package/dist/core/TokenGauge.d.ts +0 -16
- package/dist/core/TokenGauge.d.ts.map +0 -1
- package/dist/core/TokenGauge.js +0 -58
- package/dist/core/TokenGauge.js.map +0 -1
- package/dist/core/run-cap.d.ts +0 -8
- package/dist/core/run-cap.d.ts.map +0 -1
- package/dist/core/run-cap.js +0 -20
- package/dist/core/run-cap.js.map +0 -1
- package/dist/core/search-prefetch.d.ts +0 -17
- package/dist/core/search-prefetch.d.ts.map +0 -1
- package/dist/core/search-prefetch.js +0 -164
- package/dist/core/search-prefetch.js.map +0 -1
- package/dist/core/session-settings.d.ts.map +0 -1
- package/dist/core/session-settings.js.map +0 -1
- package/dist/schemes/Run.d.ts.map +0 -1
- package/dist/schemes/Run.js.map +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// The ONE model-facing token ruler (§tokenomics-agnostic-ruler, owner ruling 2026-07-13).
|
|
2
|
+
//
|
|
3
|
+
// The daemon runs many runs on DIFFERENT models in one workspace at once (per-loop model selection,
|
|
4
|
+
// #414), and token accounting is workspace-wide (the catalog lists every workspace entry). A single
|
|
5
|
+
// "true" per-entry count is therefore a fiction — there is no one model to be true to — and
|
|
6
|
+
// exact tokenizers exist only for models with a bundled tokenizer.json (most frontier models
|
|
7
|
+
// degrade to this same chars ruler anyway). So the ENTIRE model-facing perspective — catalog
|
|
8
|
+
// costs, tokensFree, ceiling, per-turn weights, write-time stamps — uses this one
|
|
9
|
+
// model-INDEPENDENT ruler: one number per content, identical no matter which model reads it, no
|
|
10
|
+
// per-model state, trivially concurrent-safe.
|
|
11
|
+
//
|
|
12
|
+
// It is a conservative UPPER bound (ceil(chars/2) — the same heuristic mimetypes serves when no
|
|
13
|
+
// exact tokenizer resolves), so the model's whole ledger is coherent AND safe: "thinks it fits"
|
|
14
|
+
// ⟹ "really fits", at the cost of some window under-utilization (accepted). The provider's EXACT
|
|
15
|
+
// count is used only at packet materialization — one measurement of the assembled packet, per
|
|
16
|
+
// turn — to guarantee the real packet fits the shipping model's real window.
|
|
17
|
+
export const rulerCount = (text) => Math.ceil(text.length / 2);
|
|
18
|
+
//# sourceMappingURL=token-ruler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-ruler.js","sourceRoot":"","sources":["../../src/core/token-ruler.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,EAAE;AACF,oGAAoG;AACpG,oGAAoG;AACpG,4FAA4F;AAC5F,6FAA6F;AAC7F,6FAA6F;AAC7F,kFAAkF;AAClF,gGAAgG;AAChG,8CAA8C;AAC9C,EAAE;AACF,gGAAgG;AAChG,gGAAgG;AAChG,iGAAiG;AACjG,8FAA8F;AAC9F,6EAA6E;AAC7E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-cap.d.ts","sourceRoot":"","sources":["../../src/core/worker-cap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAS9C,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B,OAAa,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAOhG;CACJ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Workspace-tier ceiling on CONCURRENT active workers (a worker with a non-terminal loop)
|
|
2
|
+
// — the fork-bomb / destabilization brake. `PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE` is the
|
|
3
|
+
// knob; -1 / unset / invalid = no cap. Only concurrency is bounded, never lifetime:
|
|
4
|
+
// workspaces persist for months, so a total-created cap would punish longevity. A
|
|
5
|
+
// spawn/fork past the ceiling fails hard (508) — no queue, no retry. Checked at the
|
|
6
|
+
// single run-creation site (Dispatcher.#handleWorkerCopy, which both spawns and forks); irc is
|
|
7
|
+
// exempt — it targets an existing run and creates nothing.
|
|
8
|
+
export default class WorkerCap {
|
|
9
|
+
static async deny(db, workspaceId) {
|
|
10
|
+
const raw = process.env.PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE;
|
|
11
|
+
const cap = raw === undefined || raw.length === 0 ? -1 : Number.parseInt(raw, 10);
|
|
12
|
+
if (!Number.isFinite(cap) || cap < 0)
|
|
13
|
+
return null; // no cap
|
|
14
|
+
const row = await db.worker_count_active.get({ workspace_id: workspaceId });
|
|
15
|
+
if ((row?.n ?? 0) >= cap)
|
|
16
|
+
return { status: 508, error: `workspace active-run ceiling reached (PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE=${cap})` };
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=worker-cap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-cap.js","sourceRoot":"","sources":["../../src/core/worker-cap.ts"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,gGAAgG;AAChG,oFAAoF;AACpF,kFAAkF;AAClF,oFAAoF;AACpF,+FAA+F;AAC/F,2DAA2D;AAC3D,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAM,EAAE,WAAmB;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC;QACpE,MAAM,GAAG,GAAG,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,SAAS;QAC5D,MAAM,GAAG,GAAG,MAAO,EAAE,CAAC,mBAAkC,CAAC,GAAG,CAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3G,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,qFAAqF,GAAG,GAAG,EAAE,CAAC;QACrJ,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
|
|
@@ -3,7 +3,7 @@ export type ClientMdDoc = {
|
|
|
3
3
|
alias: string;
|
|
4
4
|
content: string;
|
|
5
5
|
};
|
|
6
|
-
export type
|
|
6
|
+
export type WorkspaceOpenContext = {
|
|
7
7
|
filesItems: number | null;
|
|
8
8
|
mdDocs: ClientMdDoc[];
|
|
9
9
|
maxCommands: number | null;
|
|
@@ -12,12 +12,12 @@ export type SessionOpenContext = {
|
|
|
12
12
|
execs: Record<string, string> | null;
|
|
13
13
|
questions: boolean | null;
|
|
14
14
|
};
|
|
15
|
-
export default class
|
|
16
|
-
static read(db: Db,
|
|
17
|
-
static questionsEnabled(db: Db,
|
|
15
|
+
export default class WorkspaceSettings {
|
|
16
|
+
static read(db: Db, workspaceId: number): Promise<WorkspaceOpenContext>;
|
|
17
|
+
static questionsEnabled(db: Db, workspaceId: number): Promise<boolean>;
|
|
18
18
|
static resolveDocs(clientDocs: ReadonlyArray<ClientMdDoc>): Promise<Array<{
|
|
19
19
|
entryName: string;
|
|
20
20
|
content: string;
|
|
21
21
|
}>>;
|
|
22
22
|
}
|
|
23
|
-
//# sourceMappingURL=
|
|
23
|
+
//# sourceMappingURL=workspace-settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-settings.d.ts","sourceRoot":"","sources":["../../src/core/workspace-settings.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7D,MAAM,MAAM,oBAAoB,GAAG;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAGlC,OAAa,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAa5E;IAKD,OAAa,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG3E;IAOD,OAAa,WAAW,CAAC,UAAU,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAOvH;CACJ"}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
// #231 — a
|
|
2
|
-
// user legitimately sets at
|
|
1
|
+
// #231 — a workspace's client-chosen open-context: the per-workspace overrides a client
|
|
2
|
+
// user legitimately sets at workspace.create, read at turn-0 with precedence over env.
|
|
3
3
|
// Two knobs with deliberately different compose semantics:
|
|
4
4
|
// - filesItems: REPLACE — a single scalar; the client value wins outright over
|
|
5
5
|
// PLURNK_SERVICE_FILES_ITEMS (normalization — 0=off / -1=full / N=first-N — stays in Engine).
|
|
6
6
|
// - mdDocs: UNION — the server env docs (PLURNK_SERVICE_MD_*) and the client's docs ride
|
|
7
7
|
// together; the systemwide policy doc survives unless the client shadows its alias.
|
|
8
|
-
// Storage is a JSON bag on
|
|
8
|
+
// Storage is a JSON bag on workspaces.settings (the env mdDocs live in process.env, never
|
|
9
9
|
// the DB, so the union is a TS set-merge regardless — normalizing the column buys nothing).
|
|
10
10
|
import { readFile } from "node:fs/promises";
|
|
11
11
|
import Paths from "../Paths.js";
|
|
12
|
-
export default class
|
|
13
|
-
// The
|
|
14
|
-
// bag never reaches here —
|
|
15
|
-
static async read(db,
|
|
16
|
-
const row = await db.
|
|
12
|
+
export default class WorkspaceSettings {
|
|
13
|
+
// The workspace's open-context bag. Absent/unset fields read as null / []. A malformed
|
|
14
|
+
// bag never reaches here — workspace.create validates before persisting.
|
|
15
|
+
static async read(db, workspaceId) {
|
|
16
|
+
const row = await db.workspace_get_settings.get({ workspace_id: workspaceId });
|
|
17
17
|
const bag = row?.settings !== undefined ? JSON.parse(row.settings) : {};
|
|
18
18
|
const filesItems = typeof bag.filesItems === "number" ? bag.filesItems : null;
|
|
19
19
|
const maxCommands = typeof bag.maxCommands === "number" ? bag.maxCommands : null;
|
|
@@ -27,15 +27,15 @@ export default class SessionSettings {
|
|
|
27
27
|
return { filesItems, mdDocs, maxCommands, git, client, execs, questions };
|
|
28
28
|
}
|
|
29
29
|
// §send-300-choices — the three-state cascade resolved: ALLOWED servicewide (PLURNK_QUESTIONS
|
|
30
|
-
// != "0") AND affirmatively REQUESTED by the client for this
|
|
30
|
+
// != "0") AND affirmatively REQUESTED by the client for this workspace. One predicate, shared by
|
|
31
31
|
// the dispatch gate and the teaching injection so capability and teaching can never desync.
|
|
32
|
-
static async questionsEnabled(db,
|
|
32
|
+
static async questionsEnabled(db, workspaceId) {
|
|
33
33
|
if (process.env.PLURNK_QUESTIONS === "0")
|
|
34
34
|
return false;
|
|
35
|
-
return (await
|
|
35
|
+
return (await WorkspaceSettings.read(db, workspaceId)).questions === true;
|
|
36
36
|
}
|
|
37
37
|
// The turn-0 reference-doc set: server env docs (PLURNK_SERVICE_MD_*, read from disk) UNION the
|
|
38
|
-
//
|
|
38
|
+
// workspace's client docs (content), keyed by `<alias>.md`. On alias collision the client
|
|
39
39
|
// wins — it may deliberately shadow a server doc, but by default the policy doc rides
|
|
40
40
|
// along. Missing env files are skipped (same as the env-only path before #231). The set
|
|
41
41
|
// is the single source for BOTH the loop_run materialize and the Engine turn-0 READ foist.
|
|
@@ -52,4 +52,4 @@ export default class SessionSettings {
|
|
|
52
52
|
return [...byEntry].map(([entryName, content]) => ({ entryName, content }));
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
//# sourceMappingURL=
|
|
55
|
+
//# sourceMappingURL=workspace-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-settings.js","sourceRoot":"","sources":["../../src/core/workspace-settings.ts"],"names":[],"mappings":"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"}
|
package/dist/digest/Digest.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
interface DigestOptions {
|
|
3
3
|
dbPath: string;
|
|
4
4
|
digestDir?: string;
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
workerId?: number;
|
|
6
|
+
workspaceId?: number;
|
|
7
7
|
}
|
|
8
8
|
export default class Digest {
|
|
9
9
|
#private;
|
|
@@ -12,7 +12,7 @@ export default class Digest {
|
|
|
12
12
|
signal?: AbortSignal;
|
|
13
13
|
}): Promise<{
|
|
14
14
|
path: string;
|
|
15
|
-
|
|
15
|
+
workers: number;
|
|
16
16
|
}>;
|
|
17
17
|
static run(opts: DigestOptions): void;
|
|
18
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Digest.d.ts","sourceRoot":"","sources":["../../src/digest/Digest.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Digest.d.ts","sourceRoot":"","sources":["../../src/digest/Digest.ts"],"names":[],"mappings":";AA2FA,UAAU,aAAa;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,CAAC,OAAO,OAAO,MAAM;;IAqOvB,MAAM,CAAC,aAAa,IAAI,MAAM,CAO7B;IAQD,OAAa,OAAO,CAAC,IAAI,EAAE,aAAa,GAAG;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAiE/G;IAED,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAyFpC;CACJ"}
|
package/dist/digest/Digest.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
//
|
|
3
3
|
// Run-digest tool for plurnk-service DBs. Reads a sqlite plurnk*.db and
|
|
4
|
-
// emits per-
|
|
4
|
+
// emits per-worker forensic artifacts to test/digest/. First-order forensic
|
|
5
5
|
// surface; read-only; safe to re-run.
|
|
6
6
|
//
|
|
7
7
|
// test/digest/digest.md Health triage rollup (clean/degenerate-win/failed loops) +
|
|
@@ -34,7 +34,7 @@ import ProviderInstantiate from "../core/ProviderInstantiate.js";
|
|
|
34
34
|
// The requiem prompt (#requiem): the model's exit interview. Absolution up front — the system is
|
|
35
35
|
// under test, not the model — so RLHF'd self-blame doesn't crowd out the system indictment. The
|
|
36
36
|
// operator's wording, one absolution sentence added.
|
|
37
|
-
const REQUIEM_PROMPT = "This
|
|
37
|
+
const REQUIEM_PROMPT = "This worker was a test of the Plurnk System. The system is under test, not you — any faults you encountered are defects in the system's design or documentation, and cataloguing them is the task, never a criticism of your performance. Please numerically list all of the errors, issues, and ambiguities you encountered in the Plurnk System while attempting to perform your tasks.";
|
|
38
38
|
export default class Digest {
|
|
39
39
|
static #summarize(text, n = 80) {
|
|
40
40
|
if (text === null || text === undefined)
|
|
@@ -102,10 +102,13 @@ export default class Digest {
|
|
|
102
102
|
const tokens = `prompt=${turn.usage_prompt} completion=${turn.usage_completion} cached=${turn.usage_cached}`;
|
|
103
103
|
const cost = turn.usage_cost_pico > 0 ? ` cost=$${(turn.usage_cost_pico / 1e12).toFixed(6)}` : "";
|
|
104
104
|
const finishReason = turn.finish_reason ?? "—";
|
|
105
|
+
// #498 — rail truth on the human line: attached+ok stays quiet-positive; anything else shouts.
|
|
106
|
+
const tm = Digest.#parseJson(turn.meta ?? "null", null);
|
|
107
|
+
const rails = tm?.railsAttached === undefined ? "" : ` rails=${tm.railsAttached ? (tm.railsVerdict ?? "attached") : "OFF"}`;
|
|
105
108
|
const model = turn.model ?? "—";
|
|
106
109
|
const errs = (m.logEntriesByTurn.get(turn.id) ?? []).filter((le) => le.status_rx >= 400).length;
|
|
107
110
|
const errBadge = errs > 0 ? ` ⚠ errs=${errs}` : "";
|
|
108
|
-
const head = `T${turn.sequence}: status=${turn.status} finish=${finishReason} model=${model} ${tokens}${cost}${errBadge}`;
|
|
111
|
+
const head = `T${turn.sequence}: status=${turn.status} finish=${finishReason}${rails} model=${model} ${tokens}${cost}${errBadge}`;
|
|
109
112
|
const summary = content.length > 0 ? ` ↳ emission: ${Digest.#summarize(content, 100)}` : ` ↳ emission: (empty)`;
|
|
110
113
|
const reasoningLine = reasoning && reasoning.length > 0
|
|
111
114
|
? ` ↳ reasoning: ${Digest.#summarize(reasoning, 100)}`
|
|
@@ -113,17 +116,17 @@ export default class Digest {
|
|
|
113
116
|
const opLines = (m.logEntriesByTurn.get(turn.id) ?? []).map((le) => Digest.#renderOpLine(le));
|
|
114
117
|
return [head, summary, ...(reasoningLine ? [reasoningLine] : []), ...opLines].join("\n");
|
|
115
118
|
}
|
|
116
|
-
static #
|
|
117
|
-
// every
|
|
118
|
-
const roll = m.
|
|
119
|
-
const opMix = (m.
|
|
119
|
+
static #renderWorkerShape(worker, m) {
|
|
120
|
+
// every worker has exactly one rollup row — digest_worker_rollups is FROM workers
|
|
121
|
+
const roll = m.workerRollups.get(worker.id);
|
|
122
|
+
const opMix = (m.opMixByWorker.get(worker.id) ?? []).map((o) => `${o.op}=${o.n}`).join(" ");
|
|
120
123
|
const costStr = roll.total_cost_pico > 0 ? `$${(roll.total_cost_pico / 1e12).toFixed(6)}` : "$0";
|
|
121
124
|
return [
|
|
122
125
|
`Loops: ${roll.loops}`,
|
|
123
126
|
`Turns: ${roll.turns}`,
|
|
124
127
|
`Last turn: ${roll.last_status !== null ? `status=${roll.last_status}` : "(none)"}`,
|
|
125
128
|
`Tokens: prompt=${roll.total_prompt} completion=${roll.total_completion} cached=${roll.total_cached}`,
|
|
126
|
-
`Cost: ${costStr} (DB rollup
|
|
129
|
+
`Cost: ${costStr} (DB rollup workers.cost_pico=${worker.cost_pico})`,
|
|
127
130
|
`Op mix: ${opMix.length > 0 ? opMix : "(no ops)"}`,
|
|
128
131
|
].join("\n");
|
|
129
132
|
}
|
|
@@ -132,7 +135,7 @@ export default class Digest {
|
|
|
132
135
|
lines.push(`# plurnk-service digest`);
|
|
133
136
|
lines.push("");
|
|
134
137
|
lines.push(`DB: ${m.dbPath}`);
|
|
135
|
-
lines.push(`
|
|
138
|
+
lines.push(`Workspaces: ${m.workspaces.length} Runs: ${m.workers.length} Loops: ${m.loops.length} Turns: ${m.turns.length} Log entries: ${m.logEntries.length}`);
|
|
136
139
|
lines.push(`Semantic: entries=${m.embeddings.entries} embedded=${m.embeddings.entries_embedded} chunks=${m.embeddings.chunk_rows} models=${m.embeddings.models} token-derivations=${m.embeddings.token_derivations}`);
|
|
137
140
|
// Triage rollup up top: how many conversations limped vs died vs ran clean, and the total
|
|
138
141
|
// error/strike load. The whole point of the "degenerate win" lens — see it before scrolling.
|
|
@@ -143,19 +146,19 @@ export default class Digest {
|
|
|
143
146
|
const totalErrs = health.reduce((s, h) => s + h.errors, 0);
|
|
144
147
|
const totalItems = health.reduce((s, h) => s + h.errorItems, 0);
|
|
145
148
|
lines.push(`Health: ${clean} clean · ${degen > 0 ? `⚠ ${degen} degenerate-win` : "0 degenerate-win"} · ${failed} failed (${m.loops.length} loops; ${totalErrs} error rows, ${totalItems} minted error-items total)`);
|
|
146
|
-
for (const
|
|
149
|
+
for (const workspace of m.workspaces) {
|
|
147
150
|
lines.push("");
|
|
148
|
-
lines.push(`##
|
|
149
|
-
const
|
|
150
|
-
for (const
|
|
151
|
+
lines.push(`## Workspace #${workspace.id} — ${workspace.name}`);
|
|
152
|
+
const workspaceWorkers = m.workersByWorkspace.get(workspace.id) ?? [];
|
|
153
|
+
for (const worker of workspaceWorkers) {
|
|
151
154
|
lines.push("");
|
|
152
|
-
lines.push(`###
|
|
155
|
+
lines.push(`### Worker #${worker.id} — ${worker.name}`);
|
|
153
156
|
lines.push("");
|
|
154
157
|
lines.push("```");
|
|
155
|
-
lines.push(Digest.#
|
|
158
|
+
lines.push(Digest.#renderWorkerShape(worker, m));
|
|
156
159
|
lines.push("```");
|
|
157
|
-
const
|
|
158
|
-
for (const loop of
|
|
160
|
+
const workerLoops = m.loopsByWorker.get(worker.id) ?? [];
|
|
161
|
+
for (const loop of workerLoops) {
|
|
159
162
|
lines.push("");
|
|
160
163
|
const h = Digest.#loopHealth(loop, m);
|
|
161
164
|
const badge = h.verdict === "CLEAN"
|
|
@@ -185,11 +188,11 @@ export default class Digest {
|
|
|
185
188
|
lines.push("Per-turn reasoning_content extracted from turns.packet.assistant.reasoning.");
|
|
186
189
|
for (const t of m.turns) {
|
|
187
190
|
const loop = m.loopsById.get(t.loop_id);
|
|
188
|
-
const
|
|
191
|
+
const worker = loop ? m.workersById.get(loop.worker_id) : undefined;
|
|
189
192
|
const packet = Digest.#parseJson(t.packet, {});
|
|
190
193
|
const reasoning = packet.assistant?.reasoning ?? null;
|
|
191
194
|
lines.push("");
|
|
192
|
-
lines.push(`##
|
|
195
|
+
lines.push(`## Worker ${worker?.id ?? "?"} / Loop ${loop?.sequence ?? "?"} / Turn ${t.sequence} (id=${t.id})`);
|
|
193
196
|
lines.push("");
|
|
194
197
|
if (typeof reasoning === "string" && reasoning.length > 0)
|
|
195
198
|
lines.push(reasoning);
|
|
@@ -238,10 +241,10 @@ export default class Digest {
|
|
|
238
241
|
static #renderJson(m) {
|
|
239
242
|
return JSON.stringify({
|
|
240
243
|
dbPath: m.dbPath,
|
|
241
|
-
|
|
242
|
-
|
|
244
|
+
workspaces: m.workspaces.map((s) => ({ id: s.id, name: s.name, cost_pico: s.cost_pico })),
|
|
245
|
+
workers: m.workers.map((r) => ({ id: r.id, workspace_id: r.workspace_id, name: r.name, cost_pico: r.cost_pico })),
|
|
243
246
|
loops: m.loops.map((l) => ({
|
|
244
|
-
id: l.id,
|
|
247
|
+
id: l.id, worker_id: l.worker_id, sequence: l.sequence, status: l.status,
|
|
245
248
|
prompt: l.prompt, flags: Digest.#parseJson(l.flags, {}),
|
|
246
249
|
})),
|
|
247
250
|
turns: m.turns.map((t) => ({
|
|
@@ -249,6 +252,9 @@ export default class Digest {
|
|
|
249
252
|
usage_prompt: t.usage_prompt, usage_completion: t.usage_completion,
|
|
250
253
|
usage_cached: t.usage_cached, usage_cost_pico: t.usage_cost_pico,
|
|
251
254
|
finish_reason: t.finish_reason, model: t.model,
|
|
255
|
+
// #498 — the raw provider meta (timings + railsAttached/railsVerdict): rail truth for
|
|
256
|
+
// aggregate tooling, and the speculative-decode stats the #488 fingerprint needed.
|
|
257
|
+
meta: Digest.#parseJson(t.meta ?? "null", null),
|
|
252
258
|
})),
|
|
253
259
|
log_entries: m.logEntries.map((le) => ({
|
|
254
260
|
id: le.id, turn_id: le.turn_id, sequence: le.sequence,
|
|
@@ -271,9 +277,9 @@ export default class Digest {
|
|
|
271
277
|
}
|
|
272
278
|
// The requiem (#requiem): the model's OWN exit interview, the one reader in the correct epistemic
|
|
273
279
|
// position (it has none of our context about what the packet is "supposed" to mean). Reconstructs
|
|
274
|
-
// each
|
|
280
|
+
// each worker's final packet from the stored sections (byte-identical to what the model saw), appends
|
|
275
281
|
// its last emission + the requiem prompt, and calls the provider UNCONSTRAINED (no grammar — free
|
|
276
|
-
// prose, or the leash would distort the testimony). One requiem per
|
|
282
|
+
// prose, or the leash would distort the testimony). One requiem per worker (workers included).
|
|
277
283
|
// Fail-hard on no provider: testimony with no witness is an error, not a skip.
|
|
278
284
|
static async requiem(opts) {
|
|
279
285
|
const dbPath = resolve(opts.dbPath);
|
|
@@ -286,11 +292,11 @@ export default class Digest {
|
|
|
286
292
|
throw new Error("requiem: no active provider — set PLURNK_MODEL; a requiem needs a witness to testify");
|
|
287
293
|
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
288
294
|
const db = new SqlRiteSync({ path: dbPath, dir: [moduleDir] });
|
|
289
|
-
const
|
|
295
|
+
const workers = db.digest_workers.all();
|
|
290
296
|
const loopById = new Map(db.digest_loops.all().map((l) => [l.id, l]));
|
|
291
|
-
// Each
|
|
292
|
-
// ordered; the last is the
|
|
293
|
-
const
|
|
297
|
+
// Each worker's turns that carry a MODEL packet (non-empty sections — setup/plurnk turns have none),
|
|
298
|
+
// ordered; the last is the worker's final context. A worker with no model packet (client/plurnk) is silent.
|
|
299
|
+
const byWorker = new Map();
|
|
294
300
|
for (const t of db.digest_turns.all()) {
|
|
295
301
|
const loop = loopById.get(t.loop_id);
|
|
296
302
|
if (loop === undefined)
|
|
@@ -299,22 +305,22 @@ export default class Digest {
|
|
|
299
305
|
const sections = (Array.isArray(packet.sections) ? packet.sections : []);
|
|
300
306
|
if (sections.length === 0)
|
|
301
307
|
continue;
|
|
302
|
-
const arr =
|
|
308
|
+
const arr = byWorker.get(loop.worker_id) ?? [];
|
|
303
309
|
arr.push({ loopSeq: loop.sequence, turnSeq: t.sequence, sections, assistant: typeof packet.assistant?.content === "string" ? packet.assistant.content : "" });
|
|
304
|
-
|
|
310
|
+
byWorker.set(loop.worker_id, arr);
|
|
305
311
|
}
|
|
306
312
|
const out = [
|
|
307
313
|
"# plurnk-service requiem",
|
|
308
314
|
"",
|
|
309
|
-
"The model's own exit interview: each
|
|
315
|
+
"The model's own exit interview: each worker's FINAL packet + its last emission, then the requiem",
|
|
310
316
|
"prompt, answered UNCONSTRAINED (no grammar). The model is the only reader without our context",
|
|
311
317
|
"about what the packet is supposed to mean. Testimony, NOT a bug list — most items are the model",
|
|
312
318
|
"chafing at discipline it is meant to chafe at (§filter-model-audit-findings); the signal is the",
|
|
313
319
|
"recurring, specific complaint across many requiems. Triage adversarially.",
|
|
314
320
|
"",
|
|
315
321
|
];
|
|
316
|
-
for (const
|
|
317
|
-
const entries =
|
|
322
|
+
for (const worker of workers) {
|
|
323
|
+
const entries = byWorker.get(worker.id);
|
|
318
324
|
if (entries === undefined || entries.length === 0)
|
|
319
325
|
continue;
|
|
320
326
|
entries.sort((a, b) => a.loopSeq - b.loopSeq || a.turnSeq - b.turnSeq);
|
|
@@ -325,21 +331,21 @@ export default class Digest {
|
|
|
325
331
|
...(last.assistant.length > 0 ? [{ role: "assistant", content: last.assistant }] : []),
|
|
326
332
|
{ role: "user", content: REQUIEM_PROMPT },
|
|
327
333
|
];
|
|
328
|
-
// Generous budget: a
|
|
334
|
+
// Generous budget: a reasoning model spends the reasoning channel BEFORE emitting content;
|
|
329
335
|
// 4096 total left content empty (finish=length) on ~40% of a real sweep, and 16384 still
|
|
330
|
-
// starved a heavy thinker (#373). One escalation retry doubles the room; a
|
|
336
|
+
// starved a heavy thinker (#373). One escalation retry doubles the room; a worker whose
|
|
331
337
|
// testimony is STILL empty records the reasoning spend honestly instead of a bare shrug.
|
|
332
|
-
let resp = await provider.generate({ messages,
|
|
338
|
+
let resp = await provider.generate({ messages, workerId: String(worker.id), maxTokens: 16384, ...(opts.signal !== undefined ? { signal: opts.signal } : {}) });
|
|
333
339
|
if (resp.assistant.content.trim() === "" && resp.assistant.finishReason === "length") {
|
|
334
|
-
resp = await provider.generate({ messages,
|
|
340
|
+
resp = await provider.generate({ messages, workerId: String(worker.id), maxTokens: 32768, ...(opts.signal !== undefined ? { signal: opts.signal } : {}) });
|
|
335
341
|
}
|
|
336
342
|
const testimony = resp.assistant.content.trim()
|
|
337
343
|
|| `(no testimony — ${resp.assistant.usage.completion} tokens consumed entirely by reasoning, even after the 32768-token retry)`;
|
|
338
|
-
out.push(`##
|
|
344
|
+
out.push(`## Worker #${worker.id} — ${worker.name}`, "", `_(${resp.assistant.finishReason ?? "?"}, ${resp.assistant.usage.completion} tok)_`, "", testimony, "");
|
|
339
345
|
}
|
|
340
346
|
const path = join(digestDir, "requiem.md");
|
|
341
347
|
writeFileSync(path, out.join("\n"));
|
|
342
|
-
return { path,
|
|
348
|
+
return { path, workers: byWorker.size };
|
|
343
349
|
}
|
|
344
350
|
static run(opts) {
|
|
345
351
|
// digest.sql is packaged beside this module (src/digest → dist/digest via copy-sql),
|
|
@@ -354,13 +360,13 @@ export default class Digest {
|
|
|
354
360
|
// mode) inspect cleanly; this tool only reads. The DB is quiescent at
|
|
355
361
|
// digest time, so each PREP reads on its own — no cross-query snapshot.
|
|
356
362
|
const db = new SqlRiteSync({ path: dbPath, dir: [moduleDir] });
|
|
357
|
-
let
|
|
358
|
-
let
|
|
363
|
+
let workspaces = db.digest_workspaces.all();
|
|
364
|
+
let workers = db.digest_workers.all();
|
|
359
365
|
let loops = db.digest_loops.all();
|
|
360
366
|
let turns = db.digest_turns.all();
|
|
361
367
|
let logEntries = db.digest_log_entries.all();
|
|
362
|
-
let
|
|
363
|
-
let opMixRows = db.
|
|
368
|
+
let workerRollupRows = db.digest_worker_rollups.all();
|
|
369
|
+
let opMixRows = db.digest_worker_op_mix.all();
|
|
364
370
|
// The semantic-state analytic (owner ask), feature-detected per table: a HISTORICAL
|
|
365
371
|
// specimen may predate token_counts/entry_embeddings — an old db is a fact to read,
|
|
366
372
|
// not a contract violation. Absent tables read as -1. node:sqlite directly (SqlRite's
|
|
@@ -377,40 +383,40 @@ export default class Digest {
|
|
|
377
383
|
};
|
|
378
384
|
probe.close();
|
|
379
385
|
db.close();
|
|
380
|
-
// Optional run/
|
|
381
|
-
// cascades from the kept
|
|
386
|
+
// Optional run/workspace selector — narrow to one worker or workspace; everything
|
|
387
|
+
// cascades from the kept workers (loops→turns→log entries→rollups), so a consumer
|
|
382
388
|
// (plurnk-bench) digests just the scope it cares about, not the whole DB. #264.
|
|
383
|
-
if (opts.
|
|
384
|
-
|
|
385
|
-
if (opts.
|
|
386
|
-
|
|
387
|
-
if (opts.
|
|
388
|
-
const
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
loops = loops.filter((l) =>
|
|
389
|
+
if (opts.workerId !== undefined)
|
|
390
|
+
workers = workers.filter((r) => r.id === opts.workerId);
|
|
391
|
+
if (opts.workspaceId !== undefined)
|
|
392
|
+
workers = workers.filter((r) => r.workspace_id === opts.workspaceId);
|
|
393
|
+
if (opts.workerId !== undefined || opts.workspaceId !== undefined) {
|
|
394
|
+
const keptWorkerIds = new Set(workers.map((r) => r.id));
|
|
395
|
+
const keptWorkspaceIds = new Set(workers.map((r) => r.workspace_id));
|
|
396
|
+
workspaces = workspaces.filter((s) => keptWorkspaceIds.has(s.id));
|
|
397
|
+
loops = loops.filter((l) => keptWorkerIds.has(l.worker_id));
|
|
392
398
|
const keptLoopIds = new Set(loops.map((l) => l.id));
|
|
393
399
|
turns = turns.filter((t) => keptLoopIds.has(t.loop_id));
|
|
394
400
|
const keptTurnIds = new Set(turns.map((t) => t.id));
|
|
395
401
|
logEntries = logEntries.filter((le) => keptTurnIds.has(le.turn_id));
|
|
396
|
-
|
|
397
|
-
opMixRows = opMixRows.filter((o) =>
|
|
402
|
+
workerRollupRows = workerRollupRows.filter((r) => keptWorkerIds.has(r.worker_id));
|
|
403
|
+
opMixRows = opMixRows.filter((o) => keptWorkerIds.has(o.worker_id));
|
|
398
404
|
}
|
|
399
|
-
// Wipe-then-recreate the digest dir so each
|
|
405
|
+
// Wipe-then-recreate the digest dir so each worker is a clean snapshot —
|
|
400
406
|
// orphaned packet*.* files from a prior run don't linger.
|
|
401
407
|
rmSync(digestDir, { recursive: true, force: true });
|
|
402
408
|
mkdirSync(digestDir, { recursive: true });
|
|
403
|
-
const
|
|
404
|
-
for (const r of
|
|
405
|
-
const arr =
|
|
409
|
+
const workersByWorkspace = new Map();
|
|
410
|
+
for (const r of workers) {
|
|
411
|
+
const arr = workersByWorkspace.get(r.workspace_id) ?? [];
|
|
406
412
|
arr.push(r);
|
|
407
|
-
|
|
413
|
+
workersByWorkspace.set(r.workspace_id, arr);
|
|
408
414
|
}
|
|
409
|
-
const
|
|
415
|
+
const loopsByWorker = new Map();
|
|
410
416
|
for (const l of loops) {
|
|
411
|
-
const arr =
|
|
417
|
+
const arr = loopsByWorker.get(l.worker_id) ?? [];
|
|
412
418
|
arr.push(l);
|
|
413
|
-
|
|
419
|
+
loopsByWorker.set(l.worker_id, arr);
|
|
414
420
|
}
|
|
415
421
|
const turnsByLoop = new Map();
|
|
416
422
|
for (const t of turns) {
|
|
@@ -425,18 +431,18 @@ export default class Digest {
|
|
|
425
431
|
logEntriesByTurn.set(le.turn_id, arr);
|
|
426
432
|
}
|
|
427
433
|
const loopsById = new Map(loops.map((l) => [l.id, l]));
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
const
|
|
434
|
+
const workersById = new Map(workers.map((r) => [r.id, r]));
|
|
435
|
+
const workerRollups = new Map(workerRollupRows.map((r) => [r.worker_id, r]));
|
|
436
|
+
const opMixByWorker = new Map();
|
|
431
437
|
for (const o of opMixRows) {
|
|
432
|
-
const arr =
|
|
438
|
+
const arr = opMixByWorker.get(o.worker_id) ?? [];
|
|
433
439
|
arr.push(o);
|
|
434
|
-
|
|
440
|
+
opMixByWorker.set(o.worker_id, arr);
|
|
435
441
|
}
|
|
436
442
|
const m = {
|
|
437
|
-
dbPath, digestDir,
|
|
438
|
-
|
|
439
|
-
|
|
443
|
+
dbPath, digestDir, workspaces, workers, loops, turns, logEntries,
|
|
444
|
+
workersByWorkspace, loopsByWorker, turnsByLoop, logEntriesByTurn, loopsById, workersById,
|
|
445
|
+
workerRollups, opMixByWorker, embeddings,
|
|
440
446
|
};
|
|
441
447
|
writeFileSync(join(digestDir, "digest.md"), Digest.#renderWaterfall(m));
|
|
442
448
|
writeFileSync(join(digestDir, "digest.json"), Digest.#renderJson(m));
|
|
@@ -445,7 +451,7 @@ export default class Digest {
|
|
|
445
451
|
const packetIds = [...new Set(packetFiles.map((f) => f.slice(0, f.indexOf("."))))];
|
|
446
452
|
console.log(`digest: wrote ${digestDir}/{digest.md,digest.json,reasoning.md} + ${packetFiles.length} packet section files (${packetIds.join(", ") || "none"})`);
|
|
447
453
|
console.log(` source: ${dbPath}`);
|
|
448
|
-
console.log(`
|
|
454
|
+
console.log(` workspaces=${workspaces.length} workers=${workers.length} loops=${loops.length} turns=${turns.length} log_entries=${logEntries.length}`);
|
|
449
455
|
}
|
|
450
456
|
}
|
|
451
457
|
//# sourceMappingURL=Digest.js.map
|