@kelloggt/dsh-program-agents 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +90 -2
- package/cordis.patch.yml +5 -0
- package/dist/app/agents.d.ts +50 -0
- package/dist/app/agents.js +70 -0
- package/dist/app/agents.js.map +1 -0
- package/dist/app/conversations.d.ts +8 -0
- package/dist/app/conversations.js +26 -0
- package/dist/app/conversations.js.map +1 -0
- package/dist/app/endpoints.d.ts +36 -0
- package/dist/app/endpoints.js +162 -0
- package/dist/app/endpoints.js.map +1 -0
- package/dist/app/inbox.d.ts +61 -0
- package/dist/app/inbox.js +253 -0
- package/dist/app/inbox.js.map +1 -0
- package/dist/app/index.d.ts +70 -0
- package/dist/app/index.js +147 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/mailbox.d.ts +63 -0
- package/dist/app/mailbox.js +173 -0
- package/dist/app/mailbox.js.map +1 -0
- package/dist/app/models.d.ts +32 -0
- package/dist/app/models.js +13 -0
- package/dist/app/models.js.map +1 -0
- package/dist/app/server.d.ts +36 -0
- package/dist/app/server.js +233 -0
- package/dist/app/server.js.map +1 -0
- package/dist/app/stream-server.d.ts +9 -0
- package/dist/app/stream-server.js +77 -0
- package/dist/app/stream-server.js.map +1 -0
- package/dist/app/stream.d.ts +4 -0
- package/dist/app/stream.js +93 -0
- package/dist/app/stream.js.map +1 -0
- package/dist/app/tools.d.ts +16 -0
- package/dist/app/tools.js +24 -0
- package/dist/app/tools.js.map +1 -0
- package/dist/client/controller.d.ts +21 -0
- package/dist/client/controller.js +140 -0
- package/dist/client/controller.js.map +1 -0
- package/dist/client/index.d.ts +22 -0
- package/dist/client/index.js +14 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/model.d.ts +66 -0
- package/dist/client/model.js +147 -0
- package/dist/client/model.js.map +1 -0
- package/dist/client/models.d.ts +11 -0
- package/dist/client/models.js +27 -0
- package/dist/client/models.js.map +1 -0
- package/dist/client/transport.d.ts +64 -0
- package/dist/client/transport.js +141 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client/view.d.ts +19 -0
- package/dist/client/view.js +69 -0
- package/dist/client/view.js.map +1 -0
- package/dist/client.js +673 -0
- package/dist/conversations.d.ts +92 -0
- package/dist/conversations.js +6 -0
- package/dist/conversations.js.map +1 -0
- package/dist/endpoints.d.ts +48 -0
- package/dist/endpoints.js +43 -0
- package/dist/endpoints.js.map +1 -0
- package/dist/examples/dashboard.js +42 -0
- package/dist/examples/dynamic-server.js +27 -0
- package/dist/examples/inbox-worker.js +22 -0
- package/dist/examples/model-slots.js +19 -0
- package/dist/examples/standalone-supervisor.js +20 -0
- package/dist/host/adapter.d.ts +195 -0
- package/dist/host/adapter.js +630 -0
- package/dist/host/adapter.js.map +1 -0
- package/dist/host/agents.d.ts +35 -0
- package/dist/host/agents.js +222 -0
- package/dist/host/agents.js.map +1 -0
- package/dist/host/audit.d.ts +14 -0
- package/dist/host/audit.js +39 -0
- package/dist/host/audit.js.map +1 -0
- package/dist/host/catalog.d.ts +53 -0
- package/dist/host/catalog.js +116 -0
- package/dist/host/catalog.js.map +1 -0
- package/dist/host/contracts.d.ts +38 -0
- package/dist/host/contracts.js +2 -0
- package/dist/host/contracts.js.map +1 -0
- package/dist/host/conversations.d.ts +41 -0
- package/dist/host/conversations.js +324 -0
- package/dist/host/conversations.js.map +1 -0
- package/dist/host/events.d.ts +12 -0
- package/dist/host/events.js +106 -0
- package/dist/host/events.js.map +1 -0
- package/dist/host/http.d.ts +14 -0
- package/dist/host/http.js +30 -0
- package/dist/host/http.js.map +1 -0
- package/dist/host/index.d.ts +23 -0
- package/dist/host/index.js +86 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/installed-v10.js +27181 -0
- package/dist/host/installed-v11.js +27181 -0
- package/dist/host/installed-v12.js +27181 -0
- package/dist/host/installed-v13.js +27181 -0
- package/dist/host/installed-v2.js +27181 -0
- package/dist/host/installed-v3.js +27181 -0
- package/dist/host/installed-v4.js +27181 -0
- package/dist/host/installed-v5.js +27181 -0
- package/dist/host/installed-v6.js +27181 -0
- package/dist/host/installed-v7.js +27181 -0
- package/dist/host/installed-v8.js +27181 -0
- package/dist/host/installed-v9.js +27181 -0
- package/dist/host/mcp.d.ts +11 -0
- package/dist/host/mcp.js +42 -0
- package/dist/host/mcp.js.map +1 -0
- package/dist/host/models.d.ts +37 -0
- package/dist/host/models.js +141 -0
- package/dist/host/models.js.map +1 -0
- package/dist/host/operations.d.ts +22 -0
- package/dist/host/operations.js +72 -0
- package/dist/host/operations.js.map +1 -0
- package/dist/host/plugin.js +27181 -0
- package/dist/host/policies.d.ts +36 -0
- package/dist/host/policies.js +73 -0
- package/dist/host/policies.js.map +1 -0
- package/dist/host/requests.d.ts +57 -0
- package/dist/host/requests.js +353 -0
- package/dist/host/requests.js.map +1 -0
- package/dist/host/skills.d.ts +2 -0
- package/dist/host/skills.js +45 -0
- package/dist/host/skills.js.map +1 -0
- package/dist/host/tool-client.d.ts +19 -0
- package/dist/host/tool-client.js +62 -0
- package/dist/host/tool-client.js.map +1 -0
- package/dist/host/validation.d.ts +5 -0
- package/dist/host/validation.js +42 -0
- package/dist/host/validation.js.map +1 -0
- package/dist/mcp.d.ts +7 -0
- package/dist/mcp.js +2 -0
- package/dist/mcp.js.map +1 -0
- package/dist/models.d.ts +34 -0
- package/dist/models.js +53 -0
- package/dist/models.js.map +1 -0
- package/dist/monitoring/error-buffer.d.ts +89 -0
- package/dist/monitoring/error-buffer.js +355 -0
- package/dist/monitoring/error-buffer.js.map +1 -0
- package/dist/monitoring/fatal.d.ts +6 -0
- package/dist/monitoring/fatal.js +32 -0
- package/dist/monitoring/fatal.js.map +1 -0
- package/dist/monitoring/index.d.ts +7 -0
- package/dist/monitoring/index.js +8 -0
- package/dist/monitoring/index.js.map +1 -0
- package/dist/monitoring/logger.d.ts +45 -0
- package/dist/monitoring/logger.js +174 -0
- package/dist/monitoring/logger.js.map +1 -0
- package/dist/monitoring/schemas.d.ts +34 -0
- package/dist/monitoring/schemas.js +132 -0
- package/dist/monitoring/schemas.js.map +1 -0
- package/dist/monitoring/standard.d.ts +51 -0
- package/dist/monitoring/standard.js +140 -0
- package/dist/monitoring/standard.js.map +1 -0
- package/dist/monitoring/tailer.d.ts +41 -0
- package/dist/monitoring/tailer.js +210 -0
- package/dist/monitoring/tailer.js.map +1 -0
- package/dist/requests.d.ts +92 -0
- package/dist/requests.js +120 -0
- package/dist/requests.js.map +1 -0
- package/dist/runtime/bootstrap.d.ts +1 -0
- package/dist/runtime/bootstrap.js +25 -0
- package/dist/runtime/bootstrap.js.map +1 -0
- package/dist/runtime/definitions.d.ts +7 -0
- package/dist/runtime/definitions.js +138 -0
- package/dist/runtime/definitions.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +4 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/log-file.d.ts +24 -0
- package/dist/runtime/log-file.js +124 -0
- package/dist/runtime/log-file.js.map +1 -0
- package/dist/runtime/protocol.d.ts +60 -0
- package/dist/runtime/protocol.js +155 -0
- package/dist/runtime/protocol.js.map +1 -0
- package/dist/runtime/registry.d.ts +8 -0
- package/dist/runtime/registry.js +38 -0
- package/dist/runtime/registry.js.map +1 -0
- package/dist/runtime/supervisor.d.ts +74 -0
- package/dist/runtime/supervisor.js +881 -0
- package/dist/runtime/supervisor.js.map +1 -0
- package/dist/sdk/connection.d.ts +33 -0
- package/dist/sdk/connection.js +265 -0
- package/dist/sdk/connection.js.map +1 -0
- package/dist/sdk/index.d.ts +8 -0
- package/dist/sdk/index.js +2 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/supervision.d.ts +22 -0
- package/dist/supervision.js +32 -0
- package/dist/supervision.js.map +1 -0
- package/dist/types.d.ts +170 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/installation.md +37 -0
- package/docs/log-format.md +102 -0
- package/docs/publishing.md +45 -0
- package/docs/supervision.md +73 -0
- package/examples/dashboard.ts +31 -0
- package/examples/dsh-web-local.patch.yml +15 -0
- package/examples/dynamic-server.ts +13 -0
- package/examples/inbox-worker.ts +17 -0
- package/examples/model-slots.ts +12 -0
- package/examples/standalone-supervisor.ts +14 -0
- package/examples/watch-server.ts +12 -0
- package/package.json +134 -3
- package/schemas/dsh.http.request.v1.schema.json +14 -0
- package/schemas/dsh.program.error-batch.v1.schema.json +24 -0
- package/schemas/dsh.program.error.v1.schema.json +8 -0
- package/schemas/standard-log-record.v1.schema.json +26 -0
- package/skills/program-agents/SKILL.md +124 -0
- package/skills/program-agents/chat-sidebar.md +23 -0
- package/skills/program-agents/endpoints.md +92 -0
- package/skills/program-agents/inboxes.md +120 -0
- package/skills/program-agents/log-format.md +102 -0
- package/skills/program-agents/models.md +59 -0
- package/skills/program-agents/references/chat-sidebar/AgentSidebar.tsx +181 -0
- package/skills/program-agents/references/chat-sidebar/README.md +181 -0
- package/skills/program-agents/references/chat-sidebar/agent-sidebar.css +37 -0
- package/skills/program-agents/references/chat-sidebar/backend.ts +184 -0
- package/skills/program-agents/references/chat-sidebar/protocol.ts +214 -0
- package/skills/program-agents/schemas/dsh.http.request.v1.schema.json +14 -0
- package/skills/program-agents/schemas/dsh.program.error-batch.v1.schema.json +24 -0
- package/skills/program-agents/schemas/dsh.program.error.v1.schema.json +8 -0
- package/skills/program-agents/schemas/standard-log-record.v1.schema.json +26 -0
- package/skills/program-agents/ui-patterns.md +39 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CallToolResult, Tool } from '../mcp.js';
|
|
2
|
+
/** Preserve upstream MCP blocks when DSH's MCP adapter retained their canonical
|
|
3
|
+
* value; native DSH tools are adapted as text + structured JSON. */
|
|
4
|
+
export declare function mcpResult(result: {
|
|
5
|
+
isError?: boolean;
|
|
6
|
+
content?: unknown;
|
|
7
|
+
value?: unknown;
|
|
8
|
+
}, upstream?: boolean): CallToolResult;
|
|
9
|
+
export declare function mcpTool(schema: unknown): Tool;
|
|
10
|
+
/** Mirrors DSH MCP namespace algorithm, including hash suffix when normalized. */
|
|
11
|
+
export declare function mcpPublicName(server: string, tool: string): string;
|
package/dist/host/mcp.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CallToolResultSchema } from '@modelcontextprotocol/core';
|
|
2
|
+
import { isRecord, ProgramError } from '../runtime/protocol.js';
|
|
3
|
+
/** Preserve upstream MCP blocks when DSH's MCP adapter retained their canonical
|
|
4
|
+
* value; native DSH tools are adapted as text + structured JSON. */
|
|
5
|
+
export function mcpResult(result, upstream = false) {
|
|
6
|
+
let value;
|
|
7
|
+
if (upstream && !result.isError && isRecord(result.value) && Array.isArray(result.value.content))
|
|
8
|
+
value = { ...result.value, isError: false };
|
|
9
|
+
else {
|
|
10
|
+
const content = projectContent(result.content);
|
|
11
|
+
if (!content.length && result.value !== undefined)
|
|
12
|
+
content.push({ type: 'text', text: JSON.stringify(result.value) });
|
|
13
|
+
value = { content, ...isRecord(result.value) ? { structuredContent: result.value } : {}, isError: result.isError === true };
|
|
14
|
+
}
|
|
15
|
+
const checked = CallToolResultSchema.safeParse(value);
|
|
16
|
+
if (!checked.success)
|
|
17
|
+
throw new ProgramError('INVALID_RESULT', 'Tool result cannot be represented as MCP content');
|
|
18
|
+
return checked.data;
|
|
19
|
+
}
|
|
20
|
+
function projectContent(input) {
|
|
21
|
+
if (!Array.isArray(input))
|
|
22
|
+
return [];
|
|
23
|
+
return input.map(block => isRecord(block) && block.type === 'text' && typeof block.text === 'string' ? { type: 'text', text: block.text } : { type: 'text', text: JSON.stringify(block) });
|
|
24
|
+
}
|
|
25
|
+
export function mcpTool(schema) {
|
|
26
|
+
if (!isRecord(schema) || typeof schema.name !== 'string')
|
|
27
|
+
throw new ProgramError('INVALID_TOOL', 'Invalid native tool schema');
|
|
28
|
+
const parameters = isRecord(schema.parameters) ? schema.parameters : isRecord(schema.inputSchema) ? schema.inputSchema : { type: 'object', properties: {} };
|
|
29
|
+
return { name: schema.name, ...typeof schema.description === 'string' ? { description: schema.description } : {}, inputSchema: { ...parameters, type: 'object' } };
|
|
30
|
+
}
|
|
31
|
+
/** Mirrors DSH MCP namespace algorithm, including hash suffix when normalized. */
|
|
32
|
+
export function mcpPublicName(server, tool) {
|
|
33
|
+
const clean = `mcp__${server}__${tool}`.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
34
|
+
// Clean short names are the established common path. Hash algorithm is supplied
|
|
35
|
+
// separately below, avoiding conflating different original tool identities.
|
|
36
|
+
if (clean === `mcp__${server}__${tool}` && clean.length <= 64)
|
|
37
|
+
return clean;
|
|
38
|
+
return hashedName(server, tool, clean);
|
|
39
|
+
}
|
|
40
|
+
import { createHash } from 'node:crypto';
|
|
41
|
+
function hashedName(server, tool, clean) { return clean.slice(0, 51) + '_' + createHash('sha256').update(server + '\0' + tool).digest('hex').slice(0, 12); }
|
|
42
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/host/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ModelSelection, type ModelsDeclaration, type ModelSlotView } from '../models.js';
|
|
2
|
+
export interface ModelScope {
|
|
3
|
+
ownerId: string;
|
|
4
|
+
program: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ProgramModelStoreOptions {
|
|
7
|
+
maxRows?: number;
|
|
8
|
+
}
|
|
9
|
+
/** Durable preferences belong to a parent/program, never a run generation.
|
|
10
|
+
* The store validates shape only: trusted Host setters must validate adapter
|
|
11
|
+
* availability/efforts before committing, and programs never receive setters. */
|
|
12
|
+
export declare class ProgramModelStore {
|
|
13
|
+
private readonly db;
|
|
14
|
+
private readonly maxRows;
|
|
15
|
+
private closed;
|
|
16
|
+
constructor(path: string, options?: ProgramModelStoreOptions);
|
|
17
|
+
/** Replace app-owned metadata while retaining every human-owned override.
|
|
18
|
+
* Removed declarations become tombstones so a later redeclaration restores
|
|
19
|
+
* the preference and cannot reuse a stale revision from the previous slot. */
|
|
20
|
+
declare(scope: ModelScope, input: ModelsDeclaration | unknown): ModelSlotView[];
|
|
21
|
+
list(scope: ModelScope): ModelSlotView[];
|
|
22
|
+
get(scope: ModelScope, id: string): ModelSlotView;
|
|
23
|
+
resolve(scope: ModelScope, id: string): ModelSelection;
|
|
24
|
+
/** Human-facing trusted Host operation only. Undefined resets to the default.
|
|
25
|
+
* Even a no-op validates the revision, preventing stale browser writes. */
|
|
26
|
+
set(scope: ModelScope, id: string, selection: ModelSelection | undefined, expectedRevision: number): ModelSlotView;
|
|
27
|
+
setOverride(scope: ModelScope, id: string, selection: ModelSelection, expectedRevision: number): ModelSlotView;
|
|
28
|
+
reset(scope: ModelScope, id: string, expectedRevision: number): ModelSlotView;
|
|
29
|
+
close(): void;
|
|
30
|
+
private row;
|
|
31
|
+
private rows;
|
|
32
|
+
private view;
|
|
33
|
+
private checkScope;
|
|
34
|
+
private checkSnapshot;
|
|
35
|
+
private nextRevision;
|
|
36
|
+
private transaction;
|
|
37
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
4
|
+
import { ProgramError } from '../runtime/protocol.js';
|
|
5
|
+
import { parseModelSelection, parseModelSlotId, parseModelsDeclaration, modelSelectionsEqual, MODEL_SLOT_LIMITS, } from '../models.js';
|
|
6
|
+
/** Durable preferences belong to a parent/program, never a run generation.
|
|
7
|
+
* The store validates shape only: trusted Host setters must validate adapter
|
|
8
|
+
* availability/efforts before committing, and programs never receive setters. */
|
|
9
|
+
export class ProgramModelStore {
|
|
10
|
+
db;
|
|
11
|
+
maxRows;
|
|
12
|
+
closed = false;
|
|
13
|
+
constructor(path, options = {}) {
|
|
14
|
+
this.maxRows = options.maxRows ?? 10_000;
|
|
15
|
+
if (!Number.isSafeInteger(this.maxRows) || this.maxRows < 1 || this.maxRows > 100_000)
|
|
16
|
+
throw new ProgramError('INVALID_CONFIG', 'Invalid model slot store limit');
|
|
17
|
+
if (path !== ':memory:') {
|
|
18
|
+
path = resolve(path);
|
|
19
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
20
|
+
}
|
|
21
|
+
this.db = new DatabaseSync(path);
|
|
22
|
+
if (path !== ':memory:')
|
|
23
|
+
chmodSync(path, 0o600);
|
|
24
|
+
this.db.exec(`PRAGMA journal_mode = WAL; PRAGMA busy_timeout = 1000;
|
|
25
|
+
CREATE TABLE IF NOT EXISTS program_model_slots (
|
|
26
|
+
owner_id TEXT NOT NULL, program TEXT NOT NULL, slot TEXT NOT NULL,
|
|
27
|
+
description TEXT NOT NULL, default_selection TEXT NOT NULL,
|
|
28
|
+
override_selection TEXT, revision INTEGER NOT NULL, declared INTEGER NOT NULL,
|
|
29
|
+
PRIMARY KEY (owner_id,program,slot)
|
|
30
|
+
);`);
|
|
31
|
+
}
|
|
32
|
+
/** Replace app-owned metadata while retaining every human-owned override.
|
|
33
|
+
* Removed declarations become tombstones so a later redeclaration restores
|
|
34
|
+
* the preference and cannot reuse a stale revision from the previous slot. */
|
|
35
|
+
declare(scope, input) {
|
|
36
|
+
this.checkScope(scope);
|
|
37
|
+
const declarations = parseModelsDeclaration(input);
|
|
38
|
+
return this.transaction(() => {
|
|
39
|
+
const existing = this.rows(scope), byId = new Map(existing.map(row => [row.slot, row]));
|
|
40
|
+
const additions = Object.keys(declarations).filter(id => !byId.has(id)).length;
|
|
41
|
+
const total = this.db.prepare('SELECT count(*) AS count FROM program_model_slots').get().count;
|
|
42
|
+
if (total + additions > this.maxRows)
|
|
43
|
+
throw new ProgramError('BACKPRESSURE', 'Model slot store is full');
|
|
44
|
+
for (const row of existing)
|
|
45
|
+
if (row.declared && !Object.hasOwn(declarations, row.slot)) {
|
|
46
|
+
this.db.prepare('UPDATE program_model_slots SET declared=0,revision=? WHERE owner_id=? AND program=? AND slot=?').run(this.nextRevision(row.revision), scope.ownerId, scope.program, row.slot);
|
|
47
|
+
}
|
|
48
|
+
for (const [id, declaration] of Object.entries(declarations)) {
|
|
49
|
+
const row = byId.get(id);
|
|
50
|
+
if (!row) {
|
|
51
|
+
this.db.prepare('INSERT INTO program_model_slots (owner_id,program,slot,description,default_selection,override_selection,revision,declared) VALUES (?,?,?,?,?,NULL,1,1)').run(scope.ownerId, scope.program, id, declaration.description, JSON.stringify(declaration.default));
|
|
52
|
+
}
|
|
53
|
+
else if (!row.declared || row.description !== declaration.description || !modelSelectionsEqual(parseModelSelection(JSON.parse(row.default_selection)), declaration.default)) {
|
|
54
|
+
this.db.prepare('UPDATE program_model_slots SET description=?,default_selection=?,declared=1,revision=? WHERE owner_id=? AND program=? AND slot=?').run(declaration.description, JSON.stringify(declaration.default), this.nextRevision(row.revision), scope.ownerId, scope.program, id);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const views = this.list(scope);
|
|
58
|
+
this.checkSnapshot(views);
|
|
59
|
+
return views;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
list(scope) {
|
|
63
|
+
this.checkScope(scope);
|
|
64
|
+
return this.rows(scope).filter(row => row.declared).map(row => this.view(row));
|
|
65
|
+
}
|
|
66
|
+
get(scope, id) {
|
|
67
|
+
this.checkScope(scope);
|
|
68
|
+
parseModelSlotId(id);
|
|
69
|
+
const row = this.row(scope, id);
|
|
70
|
+
if (!row?.declared)
|
|
71
|
+
throw new ProgramError('NOT_FOUND', 'Model slot is not declared for this program');
|
|
72
|
+
return this.view(row);
|
|
73
|
+
}
|
|
74
|
+
resolve(scope, id) { return this.get(scope, id).selection; }
|
|
75
|
+
/** Human-facing trusted Host operation only. Undefined resets to the default.
|
|
76
|
+
* Even a no-op validates the revision, preventing stale browser writes. */
|
|
77
|
+
set(scope, id, selection, expectedRevision) {
|
|
78
|
+
this.checkScope(scope);
|
|
79
|
+
parseModelSlotId(id);
|
|
80
|
+
const normalized = selection === undefined ? undefined : parseModelSelection(selection);
|
|
81
|
+
if (!Number.isSafeInteger(expectedRevision) || expectedRevision < 1)
|
|
82
|
+
throw new ProgramError('INVALID_INPUT', 'Invalid model slot revision');
|
|
83
|
+
return this.transaction(() => {
|
|
84
|
+
const row = this.row(scope, id);
|
|
85
|
+
if (!row?.declared)
|
|
86
|
+
throw new ProgramError('NOT_FOUND', 'Model slot is not declared for this program');
|
|
87
|
+
if (row.revision !== expectedRevision)
|
|
88
|
+
throw new ProgramError('CONFLICT', 'Model slot changed; use the latest revision');
|
|
89
|
+
const previous = row.override_selection === null ? undefined : parseModelSelection(JSON.parse(row.override_selection));
|
|
90
|
+
if (!modelSelectionsEqual(previous, normalized)) {
|
|
91
|
+
this.db.prepare('UPDATE program_model_slots SET override_selection=?,revision=? WHERE owner_id=? AND program=? AND slot=?').run(normalized === undefined ? null : JSON.stringify(normalized), this.nextRevision(row.revision), scope.ownerId, scope.program, id);
|
|
92
|
+
}
|
|
93
|
+
this.checkSnapshot(this.list(scope));
|
|
94
|
+
return this.get(scope, id);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
setOverride(scope, id, selection, expectedRevision) { return this.set(scope, id, selection, expectedRevision); }
|
|
98
|
+
reset(scope, id, expectedRevision) { return this.set(scope, id, undefined, expectedRevision); }
|
|
99
|
+
close() { if (!this.closed) {
|
|
100
|
+
this.closed = true;
|
|
101
|
+
this.db.close();
|
|
102
|
+
} }
|
|
103
|
+
row(scope, id) {
|
|
104
|
+
return this.db.prepare('SELECT * FROM program_model_slots WHERE owner_id=? AND program=? AND slot=?').get(scope.ownerId, scope.program, id);
|
|
105
|
+
}
|
|
106
|
+
rows(scope) {
|
|
107
|
+
return this.db.prepare('SELECT * FROM program_model_slots WHERE owner_id=? AND program=? ORDER BY slot').all(scope.ownerId, scope.program);
|
|
108
|
+
}
|
|
109
|
+
view(row) {
|
|
110
|
+
const defaultSelection = parseModelSelection(JSON.parse(row.default_selection));
|
|
111
|
+
return { id: row.slot, description: row.description, default: defaultSelection, selection: row.override_selection === null ? { ...defaultSelection } : parseModelSelection(JSON.parse(row.override_selection)), overridden: row.override_selection !== null, revision: row.revision };
|
|
112
|
+
}
|
|
113
|
+
checkScope(scope) {
|
|
114
|
+
if (this.closed)
|
|
115
|
+
throw new ProgramError('CLOSED', 'Model slot store is closed');
|
|
116
|
+
if (!scope || [scope.ownerId, scope.program].some(value => typeof value !== 'string' || !value || value.length > 512 || /[\u0000-\u001f\u007f]/.test(value)))
|
|
117
|
+
throw new ProgramError('INVALID_INPUT', 'Invalid model slot scope');
|
|
118
|
+
}
|
|
119
|
+
checkSnapshot(views) {
|
|
120
|
+
if (Buffer.byteLength(JSON.stringify(views)) > MODEL_SLOT_LIMITS.snapshotBytes)
|
|
121
|
+
throw new ProgramError('LIMIT', 'Model slot snapshot exceeds 56 KiB');
|
|
122
|
+
}
|
|
123
|
+
nextRevision(value) {
|
|
124
|
+
if (!Number.isSafeInteger(value) || value >= Number.MAX_SAFE_INTEGER)
|
|
125
|
+
throw new ProgramError('LIMIT', 'Model slot revision limit reached');
|
|
126
|
+
return value + 1;
|
|
127
|
+
}
|
|
128
|
+
transaction(action) {
|
|
129
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
130
|
+
try {
|
|
131
|
+
const result = action();
|
|
132
|
+
this.db.exec('COMMIT');
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
this.db.exec('ROLLBACK');
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/host/models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProgramDefinition, ProgramSnapshot } from '../types.js';
|
|
2
|
+
/** ERROR ingestion stays independent of process lifetime, so fatal exit cannot
|
|
3
|
+
* revoke delivery before buffered reports reach their configured supervisor. */
|
|
4
|
+
export declare class ProgramOperations {
|
|
5
|
+
private directory;
|
|
6
|
+
private definition;
|
|
7
|
+
private deliver;
|
|
8
|
+
private buffers;
|
|
9
|
+
private tails;
|
|
10
|
+
private partial;
|
|
11
|
+
private stopped;
|
|
12
|
+
constructor(directory: string, definition: (run: ProgramSnapshot) => ProgramDefinition | undefined, deliver: (run: ProgramSnapshot, body: unknown, signal?: AbortSignal) => Promise<void>);
|
|
13
|
+
private key;
|
|
14
|
+
private buffer;
|
|
15
|
+
restore(run: ProgramSnapshot): void;
|
|
16
|
+
prepare(run: ProgramSnapshot): Promise<void>;
|
|
17
|
+
started(run: ProgramSnapshot): void;
|
|
18
|
+
output(run: ProgramSnapshot, chunk: string, stream: 'stdout' | 'stderr'): void;
|
|
19
|
+
failed(run: ProgramSnapshot): void;
|
|
20
|
+
terminal(run: ProgramSnapshot): void;
|
|
21
|
+
dispose(): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { ErrorBuffer, LogTailer, parseLogLine, JsonlDecoder } from '../monitoring/index.js';
|
|
3
|
+
/** ERROR ingestion stays independent of process lifetime, so fatal exit cannot
|
|
4
|
+
* revoke delivery before buffered reports reach their configured supervisor. */
|
|
5
|
+
export class ProgramOperations {
|
|
6
|
+
directory;
|
|
7
|
+
definition;
|
|
8
|
+
deliver;
|
|
9
|
+
buffers = new Map();
|
|
10
|
+
tails = new Map();
|
|
11
|
+
partial = new Map();
|
|
12
|
+
stopped = false;
|
|
13
|
+
constructor(directory, definition, deliver) {
|
|
14
|
+
this.directory = directory;
|
|
15
|
+
this.definition = definition;
|
|
16
|
+
this.deliver = deliver;
|
|
17
|
+
}
|
|
18
|
+
key(run) { return run.ownerId + '\0' + run.program; }
|
|
19
|
+
buffer(run) {
|
|
20
|
+
const key = this.key(run);
|
|
21
|
+
let buffer = this.buffers.get(key);
|
|
22
|
+
if (buffer)
|
|
23
|
+
return buffer;
|
|
24
|
+
const errors = this.definition(run)?.supervision?.errors;
|
|
25
|
+
buffer = new ErrorBuffer({ key: createHash('sha256').update(key).digest('hex'), directory: this.directory, enabled: errors?.enabled !== false, bufferMs: errors?.bufferMs ?? 1000, maxBatch: errors?.maxBatch ?? 20, cooldownMs: errors?.cooldownMs ?? 30000, deliver: (body, signal) => this.deliver(run, body, signal) });
|
|
26
|
+
this.buffers.set(key, buffer);
|
|
27
|
+
return buffer;
|
|
28
|
+
}
|
|
29
|
+
restore(run) { if (!this.stopped)
|
|
30
|
+
this.buffer(run); }
|
|
31
|
+
async prepare(run) { this.started(run); await this.tails.get(this.key(run))?.ready; }
|
|
32
|
+
started(run) {
|
|
33
|
+
if (this.stopped)
|
|
34
|
+
return;
|
|
35
|
+
this.buffer(run);
|
|
36
|
+
const path = this.definition(run)?.logsPath;
|
|
37
|
+
if (!path)
|
|
38
|
+
return;
|
|
39
|
+
const key = this.key(run);
|
|
40
|
+
if (this.tails.has(key))
|
|
41
|
+
return;
|
|
42
|
+
const tail = new LogTailer({ logsPath: path, readExisting: false, cursorPath: this.directory + '/' + createHash('sha256').update(key).digest('hex') + '.cursor.json', onRecord: async (record, pointer) => { const buffer = this.buffer(run); buffer.add(record, { ...pointer, run: { id: run.id, generation: run.generation } }); await buffer.checkpoint(); } });
|
|
43
|
+
this.tails.set(key, tail);
|
|
44
|
+
void tail.ready.catch(() => { });
|
|
45
|
+
}
|
|
46
|
+
output(run, chunk, stream) {
|
|
47
|
+
if (this.stopped)
|
|
48
|
+
return;
|
|
49
|
+
const key = run.id + ':' + run.generation + ':' + stream;
|
|
50
|
+
let decoder = this.partial.get(key);
|
|
51
|
+
if (!decoder) {
|
|
52
|
+
decoder = new JsonlDecoder(record => this.buffer(run).add(record, { path: run.logsPath, run: { id: run.id, generation: run.generation } }));
|
|
53
|
+
this.partial.set(key, decoder);
|
|
54
|
+
}
|
|
55
|
+
decoder.push(chunk);
|
|
56
|
+
}
|
|
57
|
+
failed(run) {
|
|
58
|
+
if (this.stopped)
|
|
59
|
+
return;
|
|
60
|
+
const record = parseLogLine(JSON.stringify({ timestamp: new Date().toISOString(), level: 'ERROR', log_type: 'dsh.program.error.v1', message: run.error ?? 'Program process failed', event_id: `process-${run.id}-${run.generation}`, error: { name: 'ProgramExit', message: run.error ?? 'Program process failed' }, data: { unhandled: true, run_id: run.id, generation: run.generation, logs_path: run.logsPath ?? null } }));
|
|
61
|
+
if (record)
|
|
62
|
+
this.buffer(run).add(record, { path: run.logsPath, run: { id: run.id, generation: run.generation } });
|
|
63
|
+
}
|
|
64
|
+
terminal(run) { if (this.stopped)
|
|
65
|
+
return; for (const key of this.partial.keys())
|
|
66
|
+
if (key.startsWith(run.id + ':'))
|
|
67
|
+
this.partial.delete(key); void this.buffer(run).flush().catch(() => { }); }
|
|
68
|
+
async dispose() { this.stopped = true; for (const tail of this.tails.values())
|
|
69
|
+
await tail.close(); this.tails.clear(); for (const buffer of this.buffers.values())
|
|
70
|
+
await buffer.close(); this.buffers.clear(); this.partial.clear(); }
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.js","sourceRoot":"","sources":["../../src/host/operations.ts"],"names":[],"mappings":"AAAA,OAAM,EAAC,UAAU,EAAC,MAAI,aAAa,CAAC;AAEpC,OAAM,EAAC,WAAW,EAAC,SAAS,EAAC,YAAY,EAAC,YAAY,EAAC,MAAI,wBAAwB,CAAC;AAEpF;gFACgF;AAChF,MAAM,OAAO,iBAAiB;IAGT;IAAyB;IAAsE;IAF3G,OAAO,GAAC,IAAI,GAAG,EAAsB,CAAC;IAAQ,KAAK,GAAC,IAAI,GAAG,EAAoB,CAAC;IAAQ,OAAO,GAAC,IAAI,GAAG,EAAuB,CAAC;IAC/H,OAAO,GAAC,KAAK,CAAC;IACtB,YAAoB,SAAgB,EAAS,UAA6D,EAAS,OAA6E;QAA5K,cAAS,GAAT,SAAS,CAAO;QAAS,eAAU,GAAV,UAAU,CAAmD;QAAS,YAAO,GAAP,OAAO,CAAsE;IAAE,CAAC;IAC3L,GAAG,CAAC,GAAmB,IAAE,OAAO,GAAG,CAAC,OAAO,GAAC,IAAI,GAAC,GAAG,CAAC,OAAO,CAAC,CAAA,CAAC;IAC9D,MAAM,CAAC,GAAmB;QACjC,MAAM,GAAG,GAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAAA,IAAI,MAAM,GAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAAA,IAAG,MAAM;YAAC,OAAO,MAAM,CAAC;QACjF,MAAM,MAAM,GAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC;QACvD,MAAM,GAAC,IAAI,WAAW,CAAC,EAAC,GAAG,EAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAC,SAAS,EAAC,IAAI,CAAC,SAAS,EAAC,OAAO,EAAC,MAAM,EAAE,OAAO,KAAG,KAAK,EAAC,QAAQ,EAAC,MAAM,EAAE,QAAQ,IAAE,IAAI,EAAC,QAAQ,EAAC,MAAM,EAAE,QAAQ,IAAE,EAAE,EAAC,UAAU,EAAC,MAAM,EAAE,UAAU,IAAE,KAAK,EAAC,OAAO,EAAC,CAAC,IAAI,EAAC,MAAM,EAAC,EAAE,CAAA,IAAI,CAAC,OAAO,CAAC,GAAG,EAAC,IAAI,EAAC,MAAM,CAAC,EAAC,CAAC,CAAC;QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAC,MAAM,CAAC,CAAC;QAAA,OAAO,MAAM,CAAC;IAC1U,CAAC;IACD,OAAO,CAAC,GAAmB,IAAE,IAAG,CAAC,IAAI,CAAC,OAAO;QAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA,CAAC;IAChE,KAAK,CAAC,OAAO,CAAC,GAAmB,IAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA,CAAC;IACjG,OAAO,CAAC,GAAmB;QAC1B,IAAG,IAAI,CAAC,OAAO;YAAC,OAAO;QAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACxC,MAAM,IAAI,GAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC;QAAA,IAAG,CAAC,IAAI;YAAC,OAAO;QAC1D,MAAM,GAAG,GAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAAA,IAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;YAAC,OAAO;QACtD,MAAM,IAAI,GAAC,IAAI,SAAS,CAAC,EAAC,QAAQ,EAAC,IAAI,EAAC,YAAY,EAAC,KAAK,EAAC,UAAU,EAAC,IAAI,CAAC,SAAS,GAAC,GAAG,GAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAC,cAAc,EAAC,QAAQ,EAAC,KAAK,EAAC,MAAM,EAAC,OAAO,EAAC,EAAE,GAAC,MAAM,MAAM,GAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA,MAAM,CAAC,GAAG,CAAC,MAAM,EAAC,EAAC,GAAG,OAAO,EAAC,GAAG,EAAC,EAAC,EAAE,EAAC,GAAG,CAAC,EAAE,EAAC,UAAU,EAAC,GAAG,CAAC,UAAU,EAAC,EAAC,CAAC,CAAC,CAAA,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC,CAAA,CAAC,EAAC,CAAC,CAAC;QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAC,IAAI,CAAC,CAAC;QAAA,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAE,EAAE,GAAC,CAAC,CAAC,CAAC;IACtX,CAAC;IACD,MAAM,CAAC,GAAmB,EAAC,KAAY,EAAC,MAAwB;QAC/D,IAAG,IAAI,CAAC,OAAO;YAAC,OAAO;QAAA,MAAM,GAAG,GAAC,GAAG,CAAC,EAAE,GAAC,GAAG,GAAC,GAAG,CAAC,UAAU,GAAC,GAAG,GAAC,MAAM,CAAC;QACtE,IAAI,OAAO,GAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAAA,IAAG,CAAC,OAAO,EAAC,CAAC;YAAA,OAAO,GAAC,IAAI,YAAY,CAAC,MAAM,CAAA,EAAE,CAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,EAAC,EAAC,IAAI,EAAC,GAAG,CAAC,QAAQ,EAAC,GAAG,EAAC,EAAC,EAAE,EAAC,GAAG,CAAC,EAAE,EAAC,UAAU,EAAC,GAAG,CAAC,UAAU,EAAC,EAAC,CAAC,CAAC,CAAC;YAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAC,OAAO,CAAC,CAAC;QAAA,CAAC;QAAA,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChO,CAAC;IACD,MAAM,CAAC,GAAmB;QACzB,IAAG,IAAI,CAAC,OAAO;YAAC,OAAO;QACvB,MAAM,MAAM,GAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,EAAC,SAAS,EAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAC,KAAK,EAAC,OAAO,EAAC,QAAQ,EAAC,sBAAsB,EAAC,OAAO,EAAC,GAAG,CAAC,KAAK,IAAE,wBAAwB,EAAC,QAAQ,EAAC,WAAW,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE,EAAC,KAAK,EAAC,EAAC,IAAI,EAAC,aAAa,EAAC,OAAO,EAAC,GAAG,CAAC,KAAK,IAAE,wBAAwB,EAAC,EAAC,IAAI,EAAC,EAAC,SAAS,EAAC,IAAI,EAAC,MAAM,EAAC,GAAG,CAAC,EAAE,EAAC,UAAU,EAAC,GAAG,CAAC,UAAU,EAAC,SAAS,EAAC,GAAG,CAAC,QAAQ,IAAE,IAAI,EAAC,EAAC,CAAC,CAAC,CAAC;QAC3X,IAAG,MAAM;YAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,EAAC,EAAC,IAAI,EAAC,GAAG,CAAC,QAAQ,EAAC,GAAG,EAAC,EAAC,EAAE,EAAC,GAAG,CAAC,EAAE,EAAC,UAAU,EAAC,GAAG,CAAC,UAAU,EAAC,EAAC,CAAC,CAAC;IACtG,CAAC;IACD,QAAQ,CAAC,GAAmB,IAAE,IAAG,IAAI,CAAC,OAAO;QAAC,OAAO,CAAA,KAAI,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;QAAC,IAAG,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,GAAC,GAAG,CAAC;YAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAE,EAAE,GAAC,CAAC,CAAC,CAAC,CAAA,CAAC;IAC9L,KAAK,CAAC,OAAO,KAAG,IAAI,CAAC,OAAO,GAAC,IAAI,CAAC,CAAA,KAAI,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;QAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA,KAAI,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;QAAC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA,CAAC;CACxN"}
|