negotium 0.1.11 → 0.1.13
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/README.md +11 -2
- package/dist/canonical-mcp-bridge.js +2 -3
- package/dist/canonical-mcp-bridge.js.map +1 -1
- package/dist/{chunk-gz12qcf0.js → chunk-a9y2rwxf.js} +2 -2
- package/dist/{chunk-gz12qcf0.js.map → chunk-a9y2rwxf.js.map} +3 -3
- package/dist/{chunk-j0kjscb7.js → chunk-s2gez3wg.js} +20 -2
- package/dist/{chunk-j0kjscb7.js.map → chunk-s2gez3wg.js.map} +2 -2
- package/dist/cron.js +292 -256
- package/dist/cron.js.map +23 -22
- package/dist/hosted-agent.js +3012 -17
- package/dist/hosted-agent.js.map +30 -3
- package/dist/main.js +31323 -36
- package/dist/main.js.map +198 -3
- package/dist/prompts.js +12 -9
- package/dist/prompts.js.map +5 -5
- package/dist/registry.js +10 -6
- package/dist/registry.js.map +3 -3
- package/dist/rollout.js +1 -1
- package/dist/runtime/cron/store.ts +24 -1
- package/dist/runtime/src/application/submit-user-message.ts +3 -0
- package/dist/runtime/src/index.ts +7 -0
- package/dist/runtime/src/mcp/runtime-spec.ts +3 -0
- package/dist/runtime/src/mcp/session-comm/server.ts +1 -1
- package/dist/runtime/src/platform/config.ts +2 -2
- package/dist/runtime/src/platform/mcp-config.ts +13 -0
- package/dist/runtime/src/prompts/builders.ts +20 -6
- package/dist/runtime/src/query/active-rooms.ts +2 -0
- package/dist/runtime/src/runtime/delivery-ack.ts +112 -0
- package/dist/runtime/src/runtime/errors.ts +12 -12
- package/dist/runtime/src/runtime/turn-runner.ts +12 -0
- package/dist/runtime/src/storage/api-messages.ts +7 -5
- package/dist/runtime/src/storage/api-topics.ts +5 -2
- package/dist/runtime/src/storage/forum-db.ts +4 -1
- package/dist/runtime/src/storage/runtime-turn-requests.ts +1 -0
- package/dist/runtime/src/storage/session-asks.ts +13 -5
- package/dist/runtime/src/storage/storage-contract.ts +54 -0
- package/dist/runtime/src/storage/storage-host.ts +89 -36
- package/dist/runtime/src/storage/storage-public-host.ts +16 -0
- package/dist/runtime/src/storage/storage-public.ts +21 -5
- package/dist/runtime/src/storage/token-stats.ts +1 -1
- package/dist/runtime/src/types/api.ts +2 -0
- package/dist/runtime/src/types.ts +2 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/runtime-helpers.js +29 -18
- package/dist/runtime-helpers.js.map +8 -7
- package/dist/storage.js +260 -202
- package/dist/storage.js.map +11 -9
- package/dist/types/packages/core/src/mcp/runtime-spec.d.ts +2 -0
- package/dist/types/packages/core/src/platform/mcp-config.d.ts +4 -0
- package/dist/types/packages/core/src/prompts/builders.d.ts +2 -0
- package/dist/types/packages/core/src/storage/api-messages.d.ts +6 -6
- package/dist/types/packages/core/src/storage/api-topics.d.ts +20 -2
- package/dist/types/packages/core/src/storage/forum-db.d.ts +2 -1
- package/dist/types/packages/core/src/storage/session-asks.d.ts +3 -2
- package/dist/types/packages/core/src/storage/storage-contract.d.ts +46 -0
- package/dist/types/packages/core/src/storage/storage-host.d.ts +12 -19
- package/dist/types/packages/core/src/storage/storage-public-host.d.ts +7 -0
- package/dist/types/packages/core/src/storage/storage-public.d.ts +4 -2
- package/dist/types/packages/core/src/types/api.d.ts +2 -0
- package/dist/types/packages/core/src/types.d.ts +2 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/dist/types/packages/module-cron/src/store.d.ts +22 -2
- package/dist/vault.js +2 -2
- package/dist/vault.js.map +3 -3
- package/package.json +1 -1
- package/dist/chunk-01xcc856.js +0 -149
- package/dist/chunk-01xcc856.js.map +0 -10
- package/dist/chunk-1a6kv9vt.js +0 -118
- package/dist/chunk-1a6kv9vt.js.map +0 -10
- package/dist/chunk-1b62d730.js +0 -117
- package/dist/chunk-1b62d730.js.map +0 -10
- package/dist/chunk-1k7t7bnx.js +0 -97
- package/dist/chunk-1k7t7bnx.js.map +0 -10
- package/dist/chunk-28apmz4r.js +0 -431
- package/dist/chunk-28apmz4r.js.map +0 -11
- package/dist/chunk-2fvydfkx.js +0 -141
- package/dist/chunk-2fvydfkx.js.map +0 -10
- package/dist/chunk-41ygvg5k.js +0 -2387
- package/dist/chunk-41ygvg5k.js.map +0 -26
- package/dist/chunk-4q966ttx.js +0 -371
- package/dist/chunk-4q966ttx.js.map +0 -11
- package/dist/chunk-51rcsavw.js +0 -1712
- package/dist/chunk-51rcsavw.js.map +0 -18
- package/dist/chunk-59d0shyj.js +0 -103
- package/dist/chunk-59d0shyj.js.map +0 -10
- package/dist/chunk-5fzgvn79.js +0 -3536
- package/dist/chunk-5fzgvn79.js.map +0 -20
- package/dist/chunk-61px0717.js +0 -153
- package/dist/chunk-61px0717.js.map +0 -11
- package/dist/chunk-66y0hr8c.js +0 -2136
- package/dist/chunk-66y0hr8c.js.map +0 -29
- package/dist/chunk-69k45zg8.js +0 -250
- package/dist/chunk-69k45zg8.js.map +0 -11
- package/dist/chunk-6ah5farg.js +0 -130
- package/dist/chunk-6ah5farg.js.map +0 -10
- package/dist/chunk-6d4fmea5.js +0 -117
- package/dist/chunk-6d4fmea5.js.map +0 -12
- package/dist/chunk-8cahx7xc.js +0 -46
- package/dist/chunk-8cahx7xc.js.map +0 -10
- package/dist/chunk-8f20fk4n.js +0 -252
- package/dist/chunk-8f20fk4n.js.map +0 -10
- package/dist/chunk-91ckrvk8.js +0 -318
- package/dist/chunk-91ckrvk8.js.map +0 -10
- package/dist/chunk-93krf6ng.js +0 -3069
- package/dist/chunk-93krf6ng.js.map +0 -32
- package/dist/chunk-9qx1t77a.js +0 -214
- package/dist/chunk-9qx1t77a.js.map +0 -10
- package/dist/chunk-beqhdr9w.js +0 -589
- package/dist/chunk-beqhdr9w.js.map +0 -11
- package/dist/chunk-bftftsgq.js +0 -1637
- package/dist/chunk-bftftsgq.js.map +0 -15
- package/dist/chunk-c500xe9z.js +0 -33
- package/dist/chunk-c500xe9z.js.map +0 -10
- package/dist/chunk-cjsnkvq2.js +0 -23
- package/dist/chunk-cjsnkvq2.js.map +0 -10
- package/dist/chunk-cxa7yghg.js +0 -577
- package/dist/chunk-cxa7yghg.js.map +0 -14
- package/dist/chunk-edb07sjp.js +0 -567
- package/dist/chunk-edb07sjp.js.map +0 -14
- package/dist/chunk-f361dwba.js +0 -589
- package/dist/chunk-f361dwba.js.map +0 -11
- package/dist/chunk-f5xgprg1.js +0 -59
- package/dist/chunk-f5xgprg1.js.map +0 -10
- package/dist/chunk-ffefbd3r.js +0 -17
- package/dist/chunk-ffefbd3r.js.map +0 -10
- package/dist/chunk-fswhzdf5.js +0 -64
- package/dist/chunk-fswhzdf5.js.map +0 -10
- package/dist/chunk-g144wdrs.js +0 -2907
- package/dist/chunk-g144wdrs.js.map +0 -24
- package/dist/chunk-h980hb2n.js +0 -512
- package/dist/chunk-h980hb2n.js.map +0 -13
- package/dist/chunk-h9vbhhgr.js +0 -1337
- package/dist/chunk-h9vbhhgr.js.map +0 -16
- package/dist/chunk-hwy3n31w.js +0 -21
- package/dist/chunk-hwy3n31w.js.map +0 -9
- package/dist/chunk-jtrk0khr.js +0 -108
- package/dist/chunk-jtrk0khr.js.map +0 -10
- package/dist/chunk-jzz91jwh.js +0 -70
- package/dist/chunk-jzz91jwh.js.map +0 -10
- package/dist/chunk-m9e3tbr8.js +0 -708
- package/dist/chunk-m9e3tbr8.js.map +0 -14
- package/dist/chunk-mh5mexsf.js +0 -147
- package/dist/chunk-mh5mexsf.js.map +0 -11
- package/dist/chunk-npd8n03v.js +0 -2152
- package/dist/chunk-npd8n03v.js.map +0 -15
- package/dist/chunk-pg1azh6n.js +0 -2
- package/dist/chunk-pg1azh6n.js.map +0 -9
- package/dist/chunk-qwy57177.js +0 -237
- package/dist/chunk-qwy57177.js.map +0 -11
- package/dist/chunk-r31q21h5.js +0 -53
- package/dist/chunk-r31q21h5.js.map +0 -10
- package/dist/chunk-rbvctv1n.js +0 -366
- package/dist/chunk-rbvctv1n.js.map +0 -10
- package/dist/chunk-sa874j5c.js +0 -9
- package/dist/chunk-sa874j5c.js.map +0 -10
- package/dist/chunk-sctcs5zb.js +0 -281
- package/dist/chunk-sctcs5zb.js.map +0 -10
- package/dist/chunk-tsstrcfc.js +0 -122
- package/dist/chunk-tsstrcfc.js.map +0 -10
- package/dist/chunk-wk2qf31s.js +0 -58
- package/dist/chunk-wk2qf31s.js.map +0 -10
- package/dist/chunk-wx7ag4dd.js +0 -2936
- package/dist/chunk-wx7ag4dd.js.map +0 -20
- package/dist/chunk-znx1fpfd.js +0 -61
- package/dist/chunk-znx1fpfd.js.map +0 -10
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../packages/module-cron/src/store.ts", "../../../packages/module-cron/src/schedule.ts", "../../../packages/module-cron/src/scripts.ts", "../../../packages/module-cron/src/background-sessions.ts", "../../../packages/module-cron/src/host.ts", "../../../packages/module-cron/src/context.ts", "../../../packages/module-cron/src/module.ts", "../../../packages/module-cron/src/scheduler.ts", "../../../packages/module-cron/src/summarize.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import { randomUUID } from \"node:crypto\";\nimport { type AgentKind, db as defaultDb, type EffortLevel } from \"@negotium/core\";\nimport { computeNextCronRun, normalizeCronTimezone, parseCronExpression } from \"#schedule\";\nimport { validateCronScriptName } from \"#scripts\";\n\nexport type CronRunStatus =\n | \"pending\"\n | \"running\"\n | \"succeeded\"\n | \"failed\"\n | \"aborted\"\n | \"skipped\"\n | \"interrupted\";\n\nexport interface CronJobRecord {\n id: string;\n name: string;\n ownerUserId: string;\n topicId: string;\n prompt?: string;\n script?: string;\n /** Short human label for a prompt job, generated asynchronously. */\n summary?: string;\n schedule: string;\n timezone?: string;\n enabled: boolean;\n agent?: AgentKind;\n model?: string;\n effort?: EffortLevel;\n /** @deprecated Session ownership is topic-scoped; use getCronTopicSession(). */\n sessionId?: string;\n nextRunAt: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CronJobPatch {\n name?: string;\n topicId?: string;\n prompt?: string | null;\n script?: string | null;\n summary?: string | null;\n schedule?: string;\n timezone?: string | null;\n enabled?: boolean;\n agent?: AgentKind | null;\n model?: string | null;\n effort?: EffortLevel | null;\n}\n\nexport interface CronTopicSessionRecord {\n topicId: string;\n agent: AgentKind;\n ownerUserId: string;\n sessionId: string;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CronTopicContextRecord {\n topicId: string;\n successfulRunsSinceRotation: number;\n lastRotatedAt?: string;\n updatedAt: string;\n}\n\nexport interface CronRunRecord {\n id: string;\n jobId: string;\n source: \"schedule\" | \"manual\";\n scheduledAt: string;\n startedAt?: string;\n finishedAt?: string;\n status: CronRunStatus;\n queryId?: string;\n durationMs?: number;\n outputPreview?: string;\n error?: string;\n exitCode?: number;\n}\n\ninterface JobRow {\n id: string;\n name: string;\n owner_user_id: string;\n topic_id: string;\n prompt: string;\n script: string | null;\n summary: string | null;\n schedule: string;\n timezone: string | null;\n enabled: number;\n agent: string | null;\n model: string | null;\n effort: string | null;\n session_id: string | null;\n next_run_at: string;\n created_at: string;\n updated_at: string;\n}\n\ninterface RunRow {\n id: string;\n job_id: string;\n source: \"schedule\" | \"manual\";\n scheduled_at: string;\n started_at: string | null;\n finished_at: string | null;\n status: CronRunStatus;\n query_id: string | null;\n duration_ms: number | null;\n output_preview: string | null;\n error: string | null;\n exit_code: number | null;\n topic_id: string | null;\n}\n\ninterface TopicSessionRow {\n topic_id: string;\n agent: string;\n owner_user_id: string;\n session_id: string;\n created_at: string;\n updated_at: string;\n}\n\ninterface TopicContextRow {\n topic_id: string;\n successful_runs_since_rotation: number;\n last_rotated_at: string | null;\n updated_at: string;\n}\n\ninterface TableColumnRow {\n name: string;\n}\n\nlet schemaReady = false;\n\nexport type CronDatabase = Pick<typeof defaultDb, \"exec\" | \"query\" | \"transaction\">;\n\nlet db: CronDatabase = defaultDb;\n\n/** Replace the cron persistence handle for an embedding host. */\nexport function configureCronDatabase(database: CronDatabase): () => void {\n const previous = db;\n db = database;\n schemaReady = false;\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n db = previous;\n schemaReady = false;\n };\n}\n\nexport function ensureCronSchema(): void {\n if (schemaReady) return;\n db.exec(`\n CREATE TABLE IF NOT EXISTS negotium_cron_jobs (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n owner_user_id TEXT NOT NULL,\n topic_id TEXT NOT NULL REFERENCES api_topics(id) ON DELETE CASCADE,\n prompt TEXT NOT NULL,\n script TEXT,\n summary TEXT,\n schedule TEXT NOT NULL,\n timezone TEXT,\n enabled INTEGER NOT NULL DEFAULT 1,\n agent TEXT,\n model TEXT,\n effort TEXT,\n session_id TEXT,\n next_run_at TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_negotium_cron_owner_name\n ON negotium_cron_jobs(owner_user_id, name);\n CREATE INDEX IF NOT EXISTS idx_negotium_cron_due\n ON negotium_cron_jobs(enabled, next_run_at);\n CREATE TABLE IF NOT EXISTS negotium_cron_runs (\n id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL REFERENCES negotium_cron_jobs(id) ON DELETE CASCADE,\n source TEXT NOT NULL,\n scheduled_at TEXT NOT NULL,\n started_at TEXT,\n finished_at TEXT,\n status TEXT NOT NULL,\n query_id TEXT,\n duration_ms INTEGER,\n output_preview TEXT,\n error TEXT,\n exit_code INTEGER,\n topic_id TEXT\n );\n CREATE INDEX IF NOT EXISTS idx_negotium_cron_runs_job\n ON negotium_cron_runs(job_id, scheduled_at DESC);\n CREATE TABLE IF NOT EXISTS negotium_cron_requests (\n id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL REFERENCES negotium_cron_jobs(id) ON DELETE CASCADE,\n requested_at TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS negotium_cron_cancellations (\n id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL REFERENCES negotium_cron_jobs(id) ON DELETE CASCADE,\n requested_at TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS negotium_cron_topic_sessions (\n topic_id TEXT NOT NULL,\n agent TEXT NOT NULL,\n owner_user_id TEXT NOT NULL,\n session_id TEXT NOT NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n PRIMARY KEY (topic_id, agent)\n );\n CREATE INDEX IF NOT EXISTS idx_negotium_cron_topic_sessions_owner\n ON negotium_cron_topic_sessions(owner_user_id, topic_id);\n CREATE TABLE IF NOT EXISTS negotium_cron_topic_context (\n topic_id TEXT PRIMARY KEY REFERENCES api_topics(id) ON DELETE CASCADE,\n successful_runs_since_rotation INTEGER NOT NULL DEFAULT 0,\n last_rotated_at TEXT,\n updated_at TEXT NOT NULL\n );\n `);\n const jobColumns = new Set(\n (db.query(\"PRAGMA table_info(negotium_cron_jobs)\").all() as TableColumnRow[]).map(\n (column) => column.name,\n ),\n );\n if (!jobColumns.has(\"script\")) {\n db.exec(\"ALTER TABLE negotium_cron_jobs ADD COLUMN script TEXT\");\n }\n if (!jobColumns.has(\"summary\")) {\n db.exec(\"ALTER TABLE negotium_cron_jobs ADD COLUMN summary TEXT\");\n }\n const runColumns = new Set(\n (db.query(\"PRAGMA table_info(negotium_cron_runs)\").all() as TableColumnRow[]).map(\n (column) => column.name,\n ),\n );\n if (!runColumns.has(\"exit_code\")) {\n db.exec(\"ALTER TABLE negotium_cron_runs ADD COLUMN exit_code INTEGER\");\n }\n if (!runColumns.has(\"topic_id\")) {\n db.exec(\"ALTER TABLE negotium_cron_runs ADD COLUMN topic_id TEXT\");\n }\n\n // One-time best-effort promotion from the old job-owned session model.\n // Rows are processed oldest-to-newest so the most recently updated job wins\n // when several legacy jobs targeted the same topic and provider.\n const legacySessions = db\n .query(\n `SELECT j.topic_id, COALESCE(j.agent, t.agent) AS agent,\n j.owner_user_id, j.session_id, j.created_at, j.updated_at\n FROM negotium_cron_jobs j\n LEFT JOIN api_topics t ON t.id = j.topic_id\n WHERE j.session_id IS NOT NULL AND COALESCE(j.agent, t.agent) IS NOT NULL\n ORDER BY j.updated_at ASC`,\n )\n .all() as TopicSessionRow[];\n for (const legacy of legacySessions) {\n db.query(\n `INSERT INTO negotium_cron_topic_sessions\n (topic_id,agent,owner_user_id,session_id,created_at,updated_at)\n VALUES (?,?,?,?,?,?)\n ON CONFLICT(topic_id,agent) DO UPDATE SET\n owner_user_id = excluded.owner_user_id,\n session_id = excluded.session_id,\n updated_at = excluded.updated_at`,\n ).run(\n legacy.topic_id,\n legacy.agent,\n legacy.owner_user_id,\n legacy.session_id,\n legacy.created_at,\n legacy.updated_at,\n );\n }\n if (legacySessions.length > 0) {\n db.query(\"UPDATE negotium_cron_jobs SET session_id = NULL WHERE session_id IS NOT NULL\").run();\n }\n schemaReady = true;\n}\n\nfunction toJob(row: JobRow): CronJobRecord {\n const topicAgent = row.agent\n ? row.agent\n : (\n db.query(\"SELECT agent FROM api_topics WHERE id = ?\").get(row.topic_id) as\n | { agent: string | null }\n | undefined\n )?.agent;\n const topicSession = topicAgent\n ? (db\n .query(\n \"SELECT session_id FROM negotium_cron_topic_sessions WHERE topic_id = ? AND agent = ?\",\n )\n .get(row.topic_id, topicAgent) as { session_id: string } | undefined)\n : undefined;\n return {\n id: row.id,\n name: row.name,\n ownerUserId: row.owner_user_id,\n topicId: row.topic_id,\n prompt: row.prompt.trim() || undefined,\n script: row.script?.trim() || undefined,\n summary: row.summary?.trim() || undefined,\n schedule: row.schedule,\n timezone: row.timezone ?? undefined,\n enabled: row.enabled !== 0,\n agent: (row.agent as AgentKind | null) ?? undefined,\n model: row.model ?? undefined,\n effort: (row.effort as EffortLevel | null) ?? undefined,\n sessionId: topicSession?.session_id,\n nextRunAt: row.next_run_at,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction toTopicSession(row: TopicSessionRow): CronTopicSessionRecord {\n return {\n topicId: row.topic_id,\n agent: row.agent as AgentKind,\n ownerUserId: row.owner_user_id,\n sessionId: row.session_id,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction toTopicContext(row: TopicContextRow): CronTopicContextRecord {\n return {\n topicId: row.topic_id,\n successfulRunsSinceRotation: row.successful_runs_since_rotation,\n lastRotatedAt: row.last_rotated_at ?? undefined,\n updatedAt: row.updated_at,\n };\n}\n\nexport function cronTopicSessionName(topicId: string): string {\n return `cron-${topicId}`;\n}\n\nfunction toRun(row: RunRow): CronRunRecord {\n return {\n id: row.id,\n jobId: row.job_id,\n source: row.source,\n scheduledAt: row.scheduled_at,\n startedAt: row.started_at ?? undefined,\n finishedAt: row.finished_at ?? undefined,\n status: row.status,\n queryId: row.query_id ?? undefined,\n durationMs: row.duration_ms ?? undefined,\n outputPreview: row.output_preview ?? undefined,\n error: row.error ?? undefined,\n exitCode: row.exit_code ?? undefined,\n };\n}\n\nexport function createCronJob(input: {\n name: string;\n ownerUserId: string;\n topicId: string;\n prompt?: string;\n script?: string;\n schedule: string;\n timezone?: string;\n agent?: AgentKind;\n model?: string;\n effort?: EffortLevel;\n summary?: string;\n now?: Date;\n}): CronJobRecord {\n ensureCronSchema();\n const prompt = input.prompt?.trim() || undefined;\n const script = input.script?.trim() || undefined;\n if (Boolean(prompt) === Boolean(script)) {\n throw new Error(\"cron job requires exactly one of prompt or script\");\n }\n if (script) {\n const valid = validateCronScriptName(script);\n if (!valid.ok) throw new Error(valid.error);\n }\n const otherOwner = db\n .query(\n `SELECT owner_user_id\n FROM negotium_cron_jobs\n WHERE topic_id = ? AND owner_user_id <> ?\n LIMIT 1`,\n )\n .get(input.topicId, input.ownerUserId) as { owner_user_id: string } | undefined;\n if (otherOwner) {\n throw new Error(\n `topic cron context is already owned by ${otherOwner.owner_user_id}; ` +\n \"all cron jobs in one topic must share one owner\",\n );\n }\n parseCronExpression(input.schedule);\n const timezone = input.timezone ? normalizeCronTimezone(input.timezone) : undefined;\n if (input.timezone && !timezone) throw new Error(`invalid timezone: ${input.timezone}`);\n const now = input.now ?? new Date();\n const nowIso = now.toISOString();\n const job: CronJobRecord = {\n id: randomUUID(),\n name: input.name,\n ownerUserId: input.ownerUserId,\n topicId: input.topicId,\n prompt,\n script,\n summary: input.summary?.trim() || undefined,\n schedule: input.schedule.trim(),\n timezone,\n enabled: true,\n agent: input.agent,\n model: input.model,\n effort: input.effort,\n nextRunAt: computeNextCronRun(input.schedule, now, timezone).toISOString(),\n createdAt: nowIso,\n updatedAt: nowIso,\n };\n db.query(\n `INSERT INTO negotium_cron_jobs\n (id,name,owner_user_id,topic_id,prompt,script,summary,schedule,timezone,enabled,agent,model,effort,session_id,next_run_at,created_at,updated_at)\n VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,\n ).run(\n job.id,\n job.name,\n job.ownerUserId,\n job.topicId,\n job.prompt ?? \"\",\n job.script ?? null,\n job.summary ?? null,\n job.schedule,\n job.timezone ?? null,\n 1,\n job.agent ?? null,\n job.model ?? null,\n job.effort ?? null,\n null,\n job.nextRunAt,\n job.createdAt,\n job.updatedAt,\n );\n return job;\n}\n\nexport function listCronJobs(ownerUserId?: string): CronJobRecord[] {\n ensureCronSchema();\n const rows = ownerUserId\n ? db\n .query(\"SELECT * FROM negotium_cron_jobs WHERE owner_user_id = ? ORDER BY created_at DESC\")\n .all(ownerUserId)\n : db.query(\"SELECT * FROM negotium_cron_jobs ORDER BY created_at DESC\").all();\n return (rows as JobRow[]).map(toJob);\n}\n\nexport function listEnabledCronJobs(): CronJobRecord[] {\n ensureCronSchema();\n return (\n db\n .query(\"SELECT * FROM negotium_cron_jobs WHERE enabled = 1 ORDER BY created_at DESC\")\n .all() as JobRow[]\n ).map(toJob);\n}\n\nexport function listCronJobsForTopic(topicId: string): CronJobRecord[] {\n ensureCronSchema();\n return (\n db\n .query(\"SELECT * FROM negotium_cron_jobs WHERE topic_id = ? ORDER BY created_at DESC\")\n .all(topicId) as JobRow[]\n ).map(toJob);\n}\n\nexport function listCronJobsForTopicOwner(topicId: string, ownerUserId: string): CronJobRecord[] {\n ensureCronSchema();\n return (\n db\n .query(\n \"SELECT * FROM negotium_cron_jobs WHERE topic_id = ? AND owner_user_id = ? ORDER BY created_at DESC\",\n )\n .all(topicId, ownerUserId) as JobRow[]\n ).map(toJob);\n}\n\nexport function getCronJob(id: string): CronJobRecord | null {\n ensureCronSchema();\n const row = db.query(\"SELECT * FROM negotium_cron_jobs WHERE id = ?\").get(id) as\n | JobRow\n | undefined;\n return row ? toJob(row) : null;\n}\n\nexport function getCronJobByOwnerAndName(ownerUserId: string, name: string): CronJobRecord | null {\n ensureCronSchema();\n const row = db\n .query(\"SELECT * FROM negotium_cron_jobs WHERE owner_user_id = ? AND name = ?\")\n .get(ownerUserId, name) as JobRow | undefined;\n return row ? toJob(row) : null;\n}\n\nexport function cronJobPatchChangesContext(job: CronJobRecord, patch: CronJobPatch): boolean {\n return (\n (patch.topicId !== undefined && patch.topicId !== job.topicId) ||\n (patch.prompt !== undefined && (patch.prompt?.trim() || undefined) !== job.prompt) ||\n (patch.script !== undefined && (patch.script?.trim() || undefined) !== job.script) ||\n (patch.agent !== undefined && (patch.agent ?? undefined) !== job.agent) ||\n (patch.model !== undefined && (patch.model?.trim() || undefined) !== job.model) ||\n (patch.effort !== undefined && (patch.effort ?? undefined) !== job.effort)\n );\n}\n\nexport function updateCronJob(\n id: string,\n patch: CronJobPatch,\n now = new Date(),\n): CronJobRecord | null {\n ensureCronSchema();\n const job = getCronJob(id);\n if (!job) return null;\n if (Object.values(patch).every((value) => value === undefined)) return job;\n\n const nextName = patch.name !== undefined ? patch.name.trim() : job.name;\n if (!/^[A-Za-z0-9_-]+$/.test(nextName)) {\n throw new Error(\"name must use only letters, numbers, dashes, and underscores\");\n }\n\n const nextPrompt = patch.prompt !== undefined ? patch.prompt?.trim() || undefined : job.prompt;\n const nextScript = patch.script !== undefined ? patch.script?.trim() || undefined : job.script;\n if (Boolean(nextPrompt) === Boolean(nextScript)) {\n throw new Error(\"cron job requires exactly one of prompt or script\");\n }\n if (nextScript) {\n const valid = validateCronScriptName(nextScript);\n if (!valid.ok) throw new Error(valid.error);\n }\n const nextSchedule = patch.schedule?.trim() ?? job.schedule;\n parseCronExpression(nextSchedule);\n const rawTimezone =\n patch.timezone !== undefined ? patch.timezone?.trim() || undefined : job.timezone;\n const nextTimezone = rawTimezone ? normalizeCronTimezone(rawTimezone) : undefined;\n if (rawTimezone && !nextTimezone) throw new Error(`invalid timezone: ${rawTimezone}`);\n const nextTopicId = patch.topicId?.trim() || job.topicId;\n const otherOwner = db\n .query(\n `SELECT owner_user_id FROM negotium_cron_jobs\n WHERE topic_id = ? AND owner_user_id <> ? AND id <> ? LIMIT 1`,\n )\n .get(nextTopicId, job.ownerUserId, id) as { owner_user_id: string } | undefined;\n if (otherOwner) {\n throw new Error(\n `topic cron context is already owned by ${otherOwner.owner_user_id}; ` +\n \"all cron jobs in one topic must share one owner\",\n );\n }\n\n const enabled = patch.enabled ?? job.enabled;\n const scheduleChanged = nextSchedule !== job.schedule || nextTimezone !== job.timezone;\n const nextRunAt =\n enabled && (scheduleChanged || (!job.enabled && enabled))\n ? computeNextCronRun(nextSchedule, now, nextTimezone).toISOString()\n : job.nextRunAt;\n const timestamp = now.toISOString();\n const sessionSensitiveChanged = cronJobPatchChangesContext(job, patch);\n if (sessionSensitiveChanged) {\n const active = db\n .query(\n \"SELECT 1 FROM negotium_cron_runs WHERE job_id = ? AND status IN ('pending','running') LIMIT 1\",\n )\n .get(id);\n if (active) {\n throw new Error(\"cannot change Cron context while a run is active; cancel it first\");\n }\n }\n\n db.transaction(() => {\n db.query(\n `UPDATE negotium_cron_jobs SET\n name = ?, topic_id = ?, prompt = ?, script = ?, summary = ?, schedule = ?, timezone = ?,\n enabled = ?, agent = ?, model = ?, effort = ?, next_run_at = ?, updated_at = ?\n WHERE id = ?`,\n ).run(\n nextName,\n nextTopicId,\n nextPrompt ?? \"\",\n nextScript ?? null,\n patch.summary !== undefined ? patch.summary?.trim() || null : (job.summary ?? null),\n nextSchedule,\n nextTimezone ?? null,\n enabled ? 1 : 0,\n patch.agent !== undefined ? patch.agent : (job.agent ?? null),\n patch.model !== undefined ? patch.model?.trim() || null : (job.model ?? null),\n patch.effort !== undefined ? patch.effort : (job.effort ?? null),\n nextRunAt,\n timestamp,\n id,\n );\n })();\n return getCronJob(id);\n}\n\nexport function updateCronJobSummaryIfPromptMatches(\n id: string,\n expectedPrompt: string,\n summary: string,\n): boolean {\n ensureCronSchema();\n const result = db\n .query(\"UPDATE negotium_cron_jobs SET summary = ? WHERE id = ? AND prompt = ?\")\n .run(summary.trim(), id, expectedPrompt.trim());\n return Number(result.changes) > 0;\n}\n\nexport function setCronJobEnabled(\n id: string,\n enabled: boolean,\n now = new Date(),\n): CronJobRecord | null {\n ensureCronSchema();\n const job = getCronJob(id);\n if (!job) return null;\n const nextRunAt = enabled\n ? computeNextCronRun(job.schedule, now, job.timezone).toISOString()\n : job.nextRunAt;\n db.query(\n \"UPDATE negotium_cron_jobs SET enabled = ?, next_run_at = ?, updated_at = ? WHERE id = ?\",\n ).run(enabled ? 1 : 0, nextRunAt, now.toISOString(), id);\n return getCronJob(id);\n}\n\nexport function setCronJobSessionId(id: string, sessionId: string | null): void {\n const job = getCronJob(id);\n if (!job) return;\n const topic = db.query(\"SELECT agent FROM api_topics WHERE id = ?\").get(job.topicId) as\n | { agent: string | null }\n | undefined;\n const agent = job.agent ?? (topic?.agent as AgentKind | null | undefined);\n if (!agent) return;\n if (sessionId) setCronTopicSession(job.topicId, agent, job.ownerUserId, sessionId);\n else resetCronTopicSessions(job.topicId);\n}\n\nexport function getCronTopicSession(\n topicId: string,\n agent: AgentKind,\n): CronTopicSessionRecord | null {\n ensureCronSchema();\n const row = db\n .query(\"SELECT * FROM negotium_cron_topic_sessions WHERE topic_id = ? AND agent = ?\")\n .get(topicId, agent) as TopicSessionRow | undefined;\n return row ? toTopicSession(row) : null;\n}\n\nexport function listCronTopicSessions(topicId: string): CronTopicSessionRecord[] {\n ensureCronSchema();\n return (\n db\n .query(\"SELECT * FROM negotium_cron_topic_sessions WHERE topic_id = ? ORDER BY agent\")\n .all(topicId) as TopicSessionRow[]\n ).map(toTopicSession);\n}\n\nexport function setCronTopicSession(\n topicId: string,\n agent: AgentKind,\n ownerUserId: string,\n sessionId: string,\n now = new Date(),\n): CronTopicSessionRecord {\n ensureCronSchema();\n const timestamp = now.toISOString();\n db.query(\n `INSERT INTO negotium_cron_topic_sessions\n (topic_id,agent,owner_user_id,session_id,created_at,updated_at)\n VALUES (?,?,?,?,?,?)\n ON CONFLICT(topic_id,agent) DO UPDATE SET\n owner_user_id = excluded.owner_user_id,\n session_id = excluded.session_id,\n updated_at = excluded.updated_at`,\n ).run(topicId, agent, ownerUserId, sessionId, timestamp, timestamp);\n return getCronTopicSession(topicId, agent)!;\n}\n\nexport function setCronTopicSessionIfJobUpdatedAt(\n jobId: string,\n expectedJobUpdatedAt: string,\n topicId: string,\n agent: AgentKind,\n ownerUserId: string,\n sessionId: string,\n now = new Date(),\n): CronTopicSessionRecord | null {\n ensureCronSchema();\n let updated = false;\n db.transaction(() => {\n const current = db\n .query(\"SELECT topic_id, updated_at FROM negotium_cron_jobs WHERE id = ?\")\n .get(jobId) as { topic_id: string; updated_at: string } | undefined;\n if (!current || current.topic_id !== topicId || current.updated_at !== expectedJobUpdatedAt)\n return;\n setCronTopicSession(topicId, agent, ownerUserId, sessionId, now);\n updated = true;\n })();\n return updated ? getCronTopicSession(topicId, agent) : null;\n}\n\nexport function clearCronTopicSession(topicId: string, agent: AgentKind): boolean {\n ensureCronSchema();\n const result = db\n .query(\"DELETE FROM negotium_cron_topic_sessions WHERE topic_id = ? AND agent = ?\")\n .run(topicId, agent);\n return Number(result.changes) > 0;\n}\n\nexport function resetCronTopicSessions(topicId: string): CronTopicSessionRecord[] {\n ensureCronSchema();\n const sessions = listCronTopicSessions(topicId);\n db.query(\"DELETE FROM negotium_cron_topic_sessions WHERE topic_id = ?\").run(topicId);\n return sessions;\n}\n\nexport function getCronTopicContext(topicId: string): CronTopicContextRecord | null {\n ensureCronSchema();\n const row = db\n .query(\"SELECT * FROM negotium_cron_topic_context WHERE topic_id = ?\")\n .get(topicId) as TopicContextRow | undefined;\n return row ? toTopicContext(row) : null;\n}\n\nfunction incrementCronTopicSuccess(topicId: string, now: Date): number {\n const timestamp = now.toISOString();\n db.query(\n `INSERT INTO negotium_cron_topic_context\n (topic_id,successful_runs_since_rotation,last_rotated_at,updated_at)\n VALUES (?,1,NULL,?)\n ON CONFLICT(topic_id) DO UPDATE SET\n successful_runs_since_rotation = successful_runs_since_rotation + 1,\n updated_at = excluded.updated_at`,\n ).run(topicId, timestamp);\n return getCronTopicContext(topicId)!.successfulRunsSinceRotation;\n}\n\nexport function markCronTopicContextRotated(topicId: string, now = new Date()): void {\n ensureCronSchema();\n const timestamp = now.toISOString();\n db.query(\n `INSERT INTO negotium_cron_topic_context\n (topic_id,successful_runs_since_rotation,last_rotated_at,updated_at)\n VALUES (?,0,?,?)\n ON CONFLICT(topic_id) DO UPDATE SET\n successful_runs_since_rotation = 0,\n last_rotated_at = excluded.last_rotated_at,\n updated_at = excluded.updated_at`,\n ).run(topicId, timestamp, timestamp);\n}\n\nexport function resetCronTopicContextState(topicId: string): boolean {\n ensureCronSchema();\n const result = db\n .query(\"DELETE FROM negotium_cron_topic_context WHERE topic_id = ?\")\n .run(topicId);\n return Number(result.changes) > 0;\n}\n\nexport function listOrphanedCronTopicSessions(): CronTopicSessionRecord[] {\n ensureCronSchema();\n return (\n db\n .query(\n `SELECT s.* FROM negotium_cron_topic_sessions s\n LEFT JOIN api_topics t ON t.id = s.topic_id\n WHERE t.id IS NULL\n ORDER BY s.topic_id, s.agent`,\n )\n .all() as TopicSessionRow[]\n ).map(toTopicSession);\n}\n\nexport function deleteCronJob(id: string): boolean {\n ensureCronSchema();\n const result = db.query(\"DELETE FROM negotium_cron_jobs WHERE id = ?\").run(id);\n return Number(result.changes) > 0;\n}\n\nexport function requestCronRun(jobId: string, now = new Date()): string {\n ensureCronSchema();\n const id = randomUUID();\n db.query(\"INSERT INTO negotium_cron_requests (id,job_id,requested_at) VALUES (?,?,?)\").run(\n id,\n jobId,\n now.toISOString(),\n );\n return id;\n}\n\nexport function requestCronCancel(jobId: string, now = new Date()): string {\n ensureCronSchema();\n const id = randomUUID();\n db.query(\"INSERT INTO negotium_cron_cancellations (id,job_id,requested_at) VALUES (?,?,?)\").run(\n id,\n jobId,\n now.toISOString(),\n );\n return id;\n}\n\nexport function claimCronCancellations(limit = 100): string[] {\n ensureCronSchema();\n const jobIds: string[] = [];\n db.transaction(() => {\n const rows = db\n .query(\n `SELECT id, job_id FROM negotium_cron_cancellations\n ORDER BY requested_at LIMIT ?`,\n )\n .all(limit) as Array<{ id: string; job_id: string }>;\n for (const row of rows) {\n db.query(\"DELETE FROM negotium_cron_cancellations WHERE id = ?\").run(row.id);\n if (!jobIds.includes(row.job_id)) jobIds.push(row.job_id);\n }\n })();\n return jobIds;\n}\n\nfunction insertRun(\n jobId: string,\n topicId: string,\n source: \"schedule\" | \"manual\",\n scheduledAt: string,\n): CronRunRecord {\n const run: CronRunRecord = {\n id: randomUUID(),\n jobId,\n source,\n scheduledAt,\n status: \"pending\",\n };\n db.query(\n \"INSERT INTO negotium_cron_runs (id,job_id,topic_id,source,scheduled_at,status) VALUES (?,?,?,?,?,?)\",\n ).run(run.id, run.jobId, topicId, run.source, run.scheduledAt, run.status);\n return run;\n}\n\nexport function claimCronRuns(\n now = new Date(),\n limit = 20,\n): Array<{ job: CronJobRecord; run: CronRunRecord }> {\n ensureCronSchema();\n const claimed: Array<{ job: CronJobRecord; run: CronRunRecord }> = [];\n db.transaction(() => {\n const requests = db\n .query(\n `SELECT r.id AS request_id, r.requested_at, j.*\n FROM negotium_cron_requests r JOIN negotium_cron_jobs j ON j.id = r.job_id\n ORDER BY r.requested_at LIMIT ?`,\n )\n .all(limit) as Array<JobRow & { request_id: string; requested_at: string }>;\n for (const row of requests) {\n db.query(\"DELETE FROM negotium_cron_requests WHERE id = ?\").run(row.request_id);\n claimed.push({\n job: toJob(row),\n run: insertRun(row.id, row.topic_id, \"manual\", row.requested_at),\n });\n }\n\n const remaining = Math.max(0, limit - claimed.length);\n if (remaining === 0) return;\n const due = db\n .query(\n `SELECT * FROM negotium_cron_jobs\n WHERE enabled = 1 AND next_run_at <= ? ORDER BY next_run_at LIMIT ?`,\n )\n .all(now.toISOString(), remaining) as JobRow[];\n for (const row of due) {\n const job = toJob(row);\n const run = insertRun(job.id, job.topicId, \"schedule\", job.nextRunAt);\n const nextRunAt = computeNextCronRun(job.schedule, now, job.timezone).toISOString();\n db.query(\"UPDATE negotium_cron_jobs SET next_run_at = ? WHERE id = ?\").run(nextRunAt, job.id);\n claimed.push({ job: { ...job, nextRunAt }, run });\n }\n })();\n return claimed;\n}\n\nexport function markCronRunStarted(runId: string, queryId: string, now = new Date()): void {\n ensureCronSchema();\n db.query(\n `UPDATE negotium_cron_runs\n SET status = 'running', query_id = ?, started_at = COALESCE(started_at, ?)\n WHERE id = ?`,\n ).run(queryId, now.toISOString(), runId);\n}\n\nexport function finishCronRun(\n runId: string,\n result: {\n status: Exclude<CronRunStatus, \"pending\" | \"running\">;\n outputPreview?: string;\n error?: string;\n exitCode?: number;\n },\n now = new Date(),\n): number | null {\n ensureCronSchema();\n const row = db\n .query(\n `SELECT r.started_at, r.status, COALESCE(r.topic_id, j.topic_id) AS topic_id\n FROM negotium_cron_runs r\n JOIN negotium_cron_jobs j ON j.id = r.job_id\n WHERE r.id = ?`,\n )\n .get(runId) as\n | { started_at: string | null; status: CronRunStatus; topic_id: string }\n | undefined;\n if (!row || (row.status !== \"pending\" && row.status !== \"running\")) return null;\n const durationMs = row?.started_at\n ? Math.max(0, now.getTime() - Date.parse(row.started_at))\n : null;\n let successfulRunsSinceRotation: number | null = null;\n db.transaction(() => {\n const exitCode =\n result.exitCode ??\n ({ succeeded: 0, skipped: 0, failed: 1, aborted: 130, interrupted: 137 } as const)[\n result.status\n ];\n const updated = db\n .query(\n `UPDATE negotium_cron_runs\n SET status = ?, finished_at = ?, duration_ms = ?, output_preview = ?, error = ?, exit_code = ?\n WHERE id = ? AND status IN ('pending','running')`,\n )\n .run(\n result.status,\n now.toISOString(),\n durationMs,\n result.outputPreview?.slice(0, 500) ?? null,\n result.error ?? null,\n exitCode,\n runId,\n );\n if (Number(updated.changes) > 0 && result.status === \"succeeded\") {\n successfulRunsSinceRotation = incrementCronTopicSuccess(row.topic_id, now);\n }\n })();\n return successfulRunsSinceRotation;\n}\n\nexport function listCronRuns(jobId: string, limit = 20): CronRunRecord[] {\n ensureCronSchema();\n return (\n db\n .query(\"SELECT * FROM negotium_cron_runs WHERE job_id = ? ORDER BY scheduled_at DESC LIMIT ?\")\n .all(jobId, limit) as RunRow[]\n ).map(toRun);\n}\n\nexport function getLastCronRun(jobId: string): CronRunRecord | null {\n return listCronRuns(jobId, 1)[0] ?? null;\n}\n\nexport function countCronRuns(jobId: string): number {\n ensureCronSchema();\n const row = db\n .query(\"SELECT COUNT(*) AS count FROM negotium_cron_runs WHERE job_id = ?\")\n .get(jobId) as { count: number } | undefined;\n return row?.count ?? 0;\n}\n\nexport function recoverPendingCronRuns(\n limit = 100,\n): Array<{ job: CronJobRecord; run: CronRunRecord }> {\n ensureCronSchema();\n const rows = db\n .query(\n `SELECT r.id AS run_id, r.job_id AS run_job_id, r.source AS run_source,\n r.scheduled_at AS run_scheduled_at, r.started_at AS run_started_at,\n r.finished_at AS run_finished_at, r.status AS run_status,\n r.query_id AS run_query_id, r.duration_ms AS run_duration_ms,\n r.output_preview AS run_output_preview, r.error AS run_error,\n r.exit_code AS run_exit_code,\n r.topic_id AS run_topic_id,\n j.*\n FROM negotium_cron_runs r\n JOIN negotium_cron_jobs j ON j.id = r.job_id\n WHERE r.status = 'pending'\n ORDER BY r.scheduled_at\n LIMIT ?`,\n )\n .all(limit) as Array<\n JobRow & {\n run_id: string;\n run_job_id: string;\n run_source: \"schedule\" | \"manual\";\n run_scheduled_at: string;\n run_started_at: string | null;\n run_finished_at: string | null;\n run_status: CronRunStatus;\n run_query_id: string | null;\n run_duration_ms: number | null;\n run_output_preview: string | null;\n run_error: string | null;\n run_exit_code: number | null;\n run_topic_id: string | null;\n }\n >;\n return rows.map((row) => ({\n job: toJob(row),\n run: toRun({\n id: row.run_id,\n job_id: row.run_job_id,\n source: row.run_source,\n scheduled_at: row.run_scheduled_at,\n started_at: row.run_started_at,\n finished_at: row.run_finished_at,\n status: row.run_status,\n query_id: row.run_query_id,\n duration_ms: row.run_duration_ms,\n output_preview: row.run_output_preview,\n error: row.run_error,\n exit_code: row.run_exit_code,\n topic_id: row.run_topic_id,\n }),\n }));\n}\n\nexport function finalizeOrphanedCronRuns(now = new Date()): number {\n ensureCronSchema();\n const result = db\n .query(\n `UPDATE negotium_cron_runs\n SET status = 'interrupted', finished_at = ?, exit_code = 137,\n error = 'node restarted after dispatch; final outcome is unknown'\n WHERE status = 'running'`,\n )\n .run(now.toISOString());\n return Number(result.changes);\n}\n",
|
|
6
|
-
"const FIELD_RANGES = [\n [0, 59],\n [0, 23],\n [1, 31],\n [1, 12],\n [0, 7],\n] as const;\n\ninterface ParsedField {\n values: Set<number>;\n wildcard: boolean;\n}\n\nexport interface ParsedCronExpression {\n minute: ParsedField;\n hour: ParsedField;\n dayOfMonth: ParsedField;\n month: ParsedField;\n dayOfWeek: ParsedField;\n}\n\nfunction parseNumber(raw: string, min: number, max: number): number {\n if (!/^\\d+$/.test(raw)) throw new Error(`invalid cron value: ${raw}`);\n const value = Number.parseInt(raw, 10);\n if (value < min || value > max) {\n throw new Error(`cron value ${value} is outside ${min}-${max}`);\n }\n return value;\n}\n\nfunction parseField(raw: string, min: number, max: number, normalize?: (n: number) => number) {\n const values = new Set<number>();\n const wildcard = raw === \"*\";\n for (const item of raw.split(\",\")) {\n if (!item) throw new Error(\"empty cron list item\");\n const [base, stepRaw, extra] = item.split(\"/\");\n if (extra !== undefined) throw new Error(`invalid cron step: ${item}`);\n const step = stepRaw === undefined ? 1 : parseNumber(stepRaw, 1, max - min + 1);\n let start: number;\n let end: number;\n if (base === \"*\") {\n start = min;\n end = max;\n } else if (base.includes(\"-\")) {\n const [startRaw, endRaw, rangeExtra] = base.split(\"-\");\n if (!startRaw || !endRaw || rangeExtra !== undefined) {\n throw new Error(`invalid cron range: ${base}`);\n }\n start = parseNumber(startRaw, min, max);\n end = parseNumber(endRaw, min, max);\n if (end < start) throw new Error(`descending cron range: ${base}`);\n } else {\n start = parseNumber(base, min, max);\n end = stepRaw === undefined ? start : max;\n }\n for (let value = start; value <= end; value += step) {\n values.add(normalize ? normalize(value) : value);\n }\n }\n return { values, wildcard } satisfies ParsedField;\n}\n\nexport function parseCronExpression(expression: string): ParsedCronExpression {\n const fields = expression.trim().split(/\\s+/);\n if (fields.length !== 5) throw new Error(\"cron expression must have exactly 5 fields\");\n const parsed = fields.map((field, index) => {\n const [min, max] = FIELD_RANGES[index]!;\n return parseField(\n field!,\n min,\n max,\n index === 4 ? (value) => (value === 7 ? 0 : value) : undefined,\n );\n });\n return {\n minute: parsed[0]!,\n hour: parsed[1]!,\n dayOfMonth: parsed[2]!,\n month: parsed[3]!,\n dayOfWeek: parsed[4]!,\n };\n}\n\nexport function validateCronExpression(\n expression: string,\n): { ok: true } | { ok: false; error: string } {\n try {\n parseCronExpression(expression);\n return { ok: true };\n } catch (error) {\n return { ok: false, error: error instanceof Error ? error.message : String(error) };\n }\n}\n\nconst formatters = new Map<string, Intl.DateTimeFormat>();\n\nexport function normalizeCronTimezone(timezone?: string | null): string | undefined {\n const value = timezone?.trim();\n if (!value) return undefined;\n try {\n new Intl.DateTimeFormat(\"en-US\", { timeZone: value }).format(new Date());\n return value;\n } catch {\n return undefined;\n }\n}\n\nfunction dateParts(date: Date, timezone?: string) {\n if (!timezone) {\n return {\n minute: date.getMinutes(),\n hour: date.getHours(),\n dayOfMonth: date.getDate(),\n month: date.getMonth() + 1,\n dayOfWeek: date.getDay(),\n };\n }\n let formatter = formatters.get(timezone);\n if (!formatter) {\n formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n hourCycle: \"h23\",\n });\n formatters.set(timezone, formatter);\n }\n const values = Object.fromEntries(\n formatter\n .formatToParts(date)\n .filter((part) => part.type !== \"literal\")\n .map((part) => [part.type, Number.parseInt(part.value, 10)]),\n ) as Record<string, number>;\n const year = values.year!;\n const month = values.month!;\n const dayOfMonth = values.day!;\n return {\n minute: values.minute!,\n hour: values.hour!,\n dayOfMonth,\n month,\n dayOfWeek: new Date(Date.UTC(year, month - 1, dayOfMonth)).getUTCDay(),\n };\n}\n\nexport function cronMatchesDate(\n expression: string | ParsedCronExpression,\n date: Date,\n timezone?: string,\n): boolean {\n const cron = typeof expression === \"string\" ? parseCronExpression(expression) : expression;\n const parts = dateParts(date, timezone);\n return cronMatchesParts(cron, parts);\n}\n\nfunction cronMatchesParts(\n cron: ParsedCronExpression,\n parts: ReturnType<typeof dateParts>,\n): boolean {\n if (!cron.minute.values.has(parts.minute)) return false;\n if (!cron.hour.values.has(parts.hour)) return false;\n if (!cron.month.values.has(parts.month)) return false;\n const dom = cron.dayOfMonth.values.has(parts.dayOfMonth);\n const dow = cron.dayOfWeek.values.has(parts.dayOfWeek);\n const dayMatches = cron.dayOfMonth.wildcard ? dow : cron.dayOfWeek.wildcard ? dom : dom || dow;\n return dayMatches;\n}\n\n/** Return the next matching real instant, strictly after `after`. */\nexport function computeNextCronRun(\n expression: string,\n after: Date = new Date(),\n timezone?: string,\n): Date {\n const parsed = parseCronExpression(expression);\n if (timezone && !normalizeCronTimezone(timezone))\n throw new Error(`invalid timezone: ${timezone}`);\n const candidate = new Date(after.getTime());\n candidate.setSeconds(0, 0);\n candidate.setMinutes(candidate.getMinutes() + 1);\n const deadline = candidate.getTime() + 366 * 24 * 60 * 60_000 * 5;\n while (candidate.getTime() <= deadline) {\n const parts = dateParts(candidate, timezone);\n if (cronMatchesParts(parsed, parts)) return candidate;\n\n const dom = parsed.dayOfMonth.values.has(parts.dayOfMonth);\n const dow = parsed.dayOfWeek.values.has(parts.dayOfWeek);\n const dayMatches = parsed.dayOfMonth.wildcard\n ? dow\n : parsed.dayOfWeek.wildcard\n ? dom\n : dom || dow;\n if (\n !parsed.month.values.has(parts.month) ||\n !dayMatches ||\n !parsed.hour.values.has(parts.hour)\n ) {\n // Skip to the next local-hour boundary. This keeps DST and non-whole-hour\n // timezone offsets correct while reducing sparse schedules from ~2.6m\n // minute checks to at most a few thousand hourly checks.\n const minutes = parts.minute === 0 ? 60 : 60 - parts.minute;\n candidate.setTime(candidate.getTime() + minutes * 60_000);\n continue;\n }\n candidate.setTime(candidate.getTime() + 60_000);\n }\n throw new Error(\"cron expression has no match within 5 years\");\n}\n",
|
|
7
|
-
"import { type ChildProcess, spawn } from \"node:child_process\";\nimport { existsSync, mkdirSync, readdirSync } from \"node:fs\";\nimport { resolve, sep } from \"node:path\";\nimport { WORKSPACE_DIR } from \"@negotium/core\";\n\nexport const CRON_JOBS_DIR = resolve(\n process.env.NEGOTIUM_CRON_JOBS_DIR?.trim() || resolve(WORKSPACE_DIR, \"cron\", \"jobs\"),\n);\n\nconst DEFAULT_SCRIPT_TIMEOUT_MS = 10 * 60_000;\nconst DEFAULT_OUTPUT_LIMIT_BYTES = 1024 * 1024;\nconst KILL_GRACE_MS = 2_500;\n\nexport class CronScriptError extends Error {\n constructor(\n message: string,\n readonly kind: \"error\" | \"timeout\" | \"output-limit\" = \"error\",\n ) {\n super(message);\n this.name = \"CronScriptError\";\n }\n}\n\nexport function validateCronScriptName(\n script: string,\n): { ok: true } | { ok: false; error: string } {\n if (\n !/^[A-Za-z0-9_][A-Za-z0-9_.-]*\\.py$/.test(script) ||\n script.includes(\"..\") ||\n script.includes(\"/\") ||\n script.includes(\"\\\\\")\n ) {\n return { ok: false, error: `script must be a plain .py filename under ${CRON_JOBS_DIR}` };\n }\n return { ok: true };\n}\n\nexport function resolveCronScriptPath(script: string): string {\n const valid = validateCronScriptName(script);\n if (!valid.ok) throw new Error(valid.error);\n const path = resolve(CRON_JOBS_DIR, script);\n if (!path.startsWith(`${CRON_JOBS_DIR}${sep}`))\n throw new Error(\"script path escaped jobs directory\");\n return path;\n}\n\nexport function listCronScripts(): string[] {\n mkdirSync(CRON_JOBS_DIR, { recursive: true });\n return readdirSync(CRON_JOBS_DIR, { withFileTypes: true })\n .filter((entry) => entry.isFile() && validateCronScriptName(entry.name).ok)\n .map((entry) => entry.name)\n .sort();\n}\n\nexport function cronScriptExists(script: string): boolean {\n try {\n return existsSync(resolveCronScriptPath(script));\n } catch {\n return false;\n }\n}\n\nfunction commandForScript(scriptPath: string): { executable: string; args: string[] } {\n const configured = process.env.NEGOTIUM_CRON_PYTHON?.trim();\n if (configured) return { executable: configured, args: [scriptPath] };\n if (Bun.which(\"uv\")) {\n return {\n executable: \"uv\",\n args: [\"run\", \"--project\", CRON_JOBS_DIR, \"python\", scriptPath],\n };\n }\n return { executable: \"python3\", args: [scriptPath] };\n}\n\nfunction signalTree(child: ChildProcess, signal: NodeJS.Signals): void {\n if (!child.pid) return;\n try {\n process.kill(-child.pid, signal);\n } catch {\n try {\n child.kill(signal);\n } catch {\n // The process already exited.\n }\n }\n}\n\nexport async function runCronPromptScript(options: {\n script: string;\n cwd: string;\n jobId: string;\n topicId: string;\n timeoutMs?: number;\n outputLimitBytes?: number;\n signal?: AbortSignal;\n}): Promise<string> {\n if (options.signal?.aborted) throw new CronScriptError(\"script run aborted\");\n const scriptPath = resolveCronScriptPath(options.script);\n if (!existsSync(scriptPath))\n throw new CronScriptError(`cron script not found: ${options.script}`);\n const timeoutMs = Math.max(5_000, options.timeoutMs ?? DEFAULT_SCRIPT_TIMEOUT_MS);\n const outputLimitBytes = Math.max(1024, options.outputLimitBytes ?? DEFAULT_OUTPUT_LIMIT_BYTES);\n const command = commandForScript(scriptPath);\n let child: ChildProcess;\n try {\n child = spawn(command.executable, command.args, {\n cwd: options.cwd,\n detached: true,\n env: {\n ...process.env,\n NEGOTIUM_CRON_JOB_ID: options.jobId,\n NEGOTIUM_CRON_TOPIC_ID: options.topicId,\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (error) {\n throw new CronScriptError(\n `script spawn failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n if (!child.stdout || !child.stderr) {\n signalTree(child, \"SIGKILL\");\n throw new CronScriptError(\"script spawn failed: stdout/stderr pipes unavailable\");\n }\n\n const stdout: Buffer[] = [];\n const stderr: Buffer[] = [];\n let outputBytes = 0;\n let failure: CronScriptError | undefined;\n let killTimer: ReturnType<typeof setTimeout> | undefined;\n const terminate = (error: CronScriptError) => {\n if (failure) return;\n failure = error;\n signalTree(child, \"SIGTERM\");\n killTimer = setTimeout(() => signalTree(child, \"SIGKILL\"), KILL_GRACE_MS);\n killTimer.unref?.();\n };\n const collect = (target: Buffer[]) => (chunk: Buffer | string) => {\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n outputBytes += buffer.length;\n if (outputBytes > outputLimitBytes) {\n terminate(\n new CronScriptError(`script output exceeded ${outputLimitBytes} bytes`, \"output-limit\"),\n );\n return;\n }\n target.push(buffer);\n };\n child.stdout.on(\"data\", collect(stdout));\n child.stderr.on(\"data\", collect(stderr));\n\n const timeout = setTimeout(\n () => terminate(new CronScriptError(`script exceeded ${timeoutMs}ms`, \"timeout\")),\n timeoutMs,\n );\n timeout.unref?.();\n const onAbort = () => terminate(new CronScriptError(\"script run aborted\"));\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const result = await new Promise<{ code: number | null; signal: NodeJS.Signals | null }>(\n (resolvePromise, reject) => {\n child.once(\"error\", reject);\n child.once(\"close\", (code, signal) => resolvePromise({ code, signal }));\n },\n ).finally(() => {\n clearTimeout(timeout);\n if (killTimer) clearTimeout(killTimer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n });\n\n if (failure) throw failure;\n if (result.code !== 0) {\n const detail = result.signal ? `signal ${result.signal}` : `code ${result.code ?? \"unknown\"}`;\n const errorText = Buffer.concat(stderr).toString(\"utf8\").trim();\n throw new CronScriptError(\n `script exited with ${detail}${errorText ? `: ${errorText.slice(0, 2_000)}` : \"\"}`,\n );\n }\n return Buffer.concat(stdout).toString(\"utf8\").trim();\n}\n",
|
|
8
|
-
"import {\n type BackgroundSessionDto,\n backgroundSessionProgress,\n getTopic,\n isParticipant,\n listRuntimeTurnLeases,\n} from \"@negotium/core\";\nimport type { CronJobRecord, CronRunRecord } from \"#store\";\nimport { getLastCronRun, listCronJobs } from \"#store\";\n\ninterface DisplayRun {\n job: CronJobRecord;\n run: CronRunRecord;\n}\n\nfunction promptForJob(job: CronJobRecord): string {\n return job.prompt ?? `Script: ${job.script ?? \"unknown\"}`;\n}\n\nfunction latestRun(jobs: CronJobRecord[]): DisplayRun | undefined {\n return jobs\n .flatMap((job): DisplayRun[] => {\n const run = getLastCronRun(job.id);\n return run ? [{ job, run }] : [];\n })\n .sort((left, right) => right.run.scheduledAt.localeCompare(left.run.scheduledAt))[0];\n}\n\nexport function listCronBackgroundSessions(userId: string): BackgroundSessionDto[] {\n const jobsByTopic = new Map<string, CronJobRecord[]>();\n for (const job of listCronJobs()) {\n const topic = getTopic(job.topicId);\n if (!topic || !isParticipant(topic, userId)) continue;\n const jobs = jobsByTopic.get(job.topicId) ?? [];\n jobs.push(job);\n jobsByTopic.set(job.topicId, jobs);\n }\n\n const cronLeases = listRuntimeTurnLeases().filter((lease) => lease.origin.startsWith(\"cron:\"));\n return [...jobsByTopic].flatMap(([topicId, jobs]): BackgroundSessionDto[] => {\n const topic = getTopic(topicId);\n if (!topic) return [];\n const lease = cronLeases.find((candidate) => candidate.topicId === topicId);\n const activeJobId = lease?.origin.split(\":\")[1];\n const recent = latestRun(jobs);\n const displayJob =\n jobs.find((job) => job.id === activeJobId) ??\n (recent?.job.id ? jobs.find((job) => job.id === recent.job.id) : undefined) ??\n jobs[0];\n if (!displayJob) return [];\n\n const queryId = lease?.queryId ?? recent?.run.queryId;\n const progress = queryId ? backgroundSessionProgress(topicId, queryId) : undefined;\n const enabledJobs = jobs.filter((job) => job.enabled);\n const nextJob = [...enabledJobs].sort((left, right) =>\n left.nextRunAt.localeCompare(right.nextRunAt),\n )[0];\n const steps = progress?.steps.length\n ? progress.steps\n : [\n ...(recent ? [`Last run: ${recent.job.name} · ${recent.run.status}`] : [\"No runs yet\"]),\n ...(nextJob ? [`Next: ${nextJob.name} · ${nextJob.nextRunAt}`] : []),\n ];\n\n return [\n {\n id: `cron:${topicId}`,\n kind: \"cron\",\n title: topic.title,\n topicId,\n startedAt:\n (lease ? new Date(lease.startedAt).toISOString() : recent?.run.startedAt) ??\n displayJob.createdAt,\n status: lease\n ? lease.abortRequested\n ? \"Stopping\"\n : (progress?.status ?? \"Running\")\n : enabledJobs.length > 0\n ? \"Scheduled\"\n : \"Paused\",\n active: Boolean(lease),\n agent: displayJob.agent ?? topic.agent,\n model: displayJob.model ?? topic.effectiveModel ?? topic.defaultModel,\n effort: displayJob.effort ?? topic.effectiveEffort ?? topic.defaultEffort,\n prompt: promptForJob(displayJob),\n promptTitle: `Prompt · ${displayJob.name}`,\n steps,\n },\n ];\n });\n}\n",
|
|
9
|
-
"import {\n abortRoom,\n buildStdioMcpServer,\n getRoomQuery,\n getTopic,\n purgeTopicLogs,\n registerBackgroundSessionProvider,\n registerRuntimeMcpServer,\n resolveTopicWorkspaceDir,\n rotateTopicLogs,\n} from \"@negotium/core\";\nimport type { CronDispatch } from \"#scheduler\";\n\nexport type CronAuthorizationAction = \"cron:admin\";\n\nexport interface CronAuthorizationResource {\n type: \"cron\";\n}\n\n/** Host-owned services required when the cron module is embedded. */\nexport interface CronHost {\n dispatch?: CronDispatch;\n getTopic: typeof getTopic;\n getRoomQuery: typeof getRoomQuery;\n abortRoom: typeof abortRoom;\n resolveTopicWorkspaceDir: typeof resolveTopicWorkspaceDir;\n purgeTopicLogs: typeof purgeTopicLogs;\n rotateTopicLogs: typeof rotateTopicLogs;\n registerRuntimeMcpServer: typeof registerRuntimeMcpServer;\n registerBackgroundSessionProvider: typeof registerBackgroundSessionProvider;\n buildStdioMcpServer: typeof buildStdioMcpServer;\n authorize?(\n userId: string,\n action: CronAuthorizationAction,\n resource: CronAuthorizationResource,\n ): boolean;\n}\n\nconst defaultHost: CronHost = {\n getTopic,\n getRoomQuery,\n abortRoom,\n resolveTopicWorkspaceDir,\n purgeTopicLogs,\n rotateTopicLogs,\n registerRuntimeMcpServer,\n registerBackgroundSessionProvider,\n buildStdioMcpServer,\n};\n\nlet activeHost: CronHost = defaultHost;\n\nexport function cronHost(): CronHost {\n return activeHost;\n}\n\n/** Configure one embedded cron module. Returns a disposer for shutdown/tests. */\nexport function configureCronHost(overrides: Partial<CronHost>): () => void {\n const previous = activeHost;\n activeHost = { ...previous, ...overrides };\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n activeHost = previous;\n };\n}\n",
|
|
10
|
-
"import { logger } from \"@negotium/core\";\nimport { cronHost } from \"#host\";\nimport {\n type CronJobPatch,\n type CronJobRecord,\n cronJobPatchChangesContext,\n cronTopicSessionName,\n getCronJob,\n listCronJobsForTopic,\n listCronTopicSessions,\n markCronTopicContextRotated,\n resetCronTopicContextState,\n resetCronTopicSessions,\n updateCronJob,\n} from \"#store\";\n\nexport const CRON_CONTEXT_ROTATE_EVERY = 5;\nexport const CRON_CONTEXT_RETAIN_TURNS = 5;\n\n/** Update one job and fully discard shared history affected by runtime/source changes. */\nexport async function updateCronJobWithContextReset(\n id: string,\n patch: CronJobPatch,\n now = new Date(),\n): Promise<CronJobRecord | null> {\n const previous = getCronJob(id);\n if (!previous) return null;\n const resetRequired = cronJobPatchChangesContext(previous, patch);\n const updated = updateCronJob(id, patch, now);\n if (!updated || !resetRequired) return updated;\n\n await resetCronTopicContext(previous.topicId, [previous.ownerUserId]);\n if (updated.topicId !== previous.topicId) {\n await resetCronTopicContext(updated.topicId, [updated.ownerUserId]);\n }\n return getCronJob(id);\n}\n\n/** Purge the shared Cron log and every provider rollout owned by one topic. */\nexport async function resetCronTopicContext(\n topicId: string,\n extraOwnerUserIds: Iterable<string> = [],\n): Promise<number> {\n const host = cronHost();\n const sessions = listCronTopicSessions(topicId);\n const owners = new Set([\n ...sessions.map((session) => session.ownerUserId),\n ...listCronJobsForTopic(topicId).map((job) => job.ownerUserId),\n ...extraOwnerUserIds,\n ]);\n for (const ownerUserId of owners) {\n await host.purgeTopicLogs({\n userId: ownerUserId,\n topicName: cronTopicSessionName(topicId),\n cwd: host.resolveTopicWorkspaceDir(topicId),\n extraSessions: sessions\n .filter((session) => session.ownerUserId === ownerUserId)\n .map((session) => ({ agent: session.agent, sessionId: session.sessionId })),\n });\n }\n const removed = resetCronTopicSessions(topicId).length;\n resetCronTopicContextState(topicId);\n logger.info({ topicId, sessions: removed, owners: owners.size }, \"cron: reset topic context\");\n return removed;\n}\n\nexport interface CronTopicRotationResult {\n rotated: boolean;\n clearedProviderSessions: number;\n totalTurns: number;\n retainedTurns: number;\n}\n\n/** Rotate provider sessions while carrying a bounded tail into the next run. */\nexport async function rotateCronTopicContext(\n topicId: string,\n retainTurns = CRON_CONTEXT_RETAIN_TURNS,\n): Promise<CronTopicRotationResult> {\n const host = cronHost();\n const sessions = listCronTopicSessions(topicId);\n const jobs = listCronJobsForTopic(topicId);\n const owners = new Set([\n ...sessions.map((session) => session.ownerUserId),\n ...jobs.map((job) => job.ownerUserId),\n ]);\n let totalTurns = 0;\n let retained = 0;\n\n for (const ownerUserId of owners) {\n const result = await host.rotateTopicLogs({\n userId: ownerUserId,\n topicName: cronTopicSessionName(topicId),\n cwd: host.resolveTopicWorkspaceDir(topicId),\n retainTurns,\n extraSessions: sessions\n .filter((session) => session.ownerUserId === ownerUserId)\n .map((session) => ({ agent: session.agent, sessionId: session.sessionId })),\n });\n if (!result.rotated) {\n return {\n rotated: false,\n clearedProviderSessions: 0,\n totalTurns: result.totalTurns,\n retainedTurns: result.retainedTurns,\n };\n }\n totalTurns = Math.max(totalTurns, result.totalTurns);\n retained = Math.max(retained, result.retainedTurns);\n }\n\n const clearedProviderSessions = resetCronTopicSessions(topicId).length;\n markCronTopicContextRotated(topicId);\n logger.info(\n { topicId, clearedProviderSessions, totalTurns, retainedTurns: retained },\n \"cron: rotated bounded topic context\",\n );\n return {\n rotated: true,\n clearedProviderSessions,\n totalTurns,\n retainedTurns: retained,\n };\n}\n",
|
|
11
|
-
"import { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n cancelDeferredInject,\n logger,\n type NegotiumNodeModule,\n triggerTopicAiTurn,\n} from \"@negotium/core\";\nimport { listCronBackgroundSessions } from \"#background-sessions\";\nimport { resetCronTopicContext } from \"#context\";\nimport { type CronHost, configureCronHost, cronHost } from \"#host\";\nimport { CronScheduler, type CronSchedulerOptions } from \"#scheduler\";\nimport { runCronPromptScript } from \"#scripts\";\nimport {\n type CronDatabase,\n configureCronDatabase,\n ensureCronSchema,\n listOrphanedCronTopicSessions,\n} from \"#store\";\n\nconst SOURCE_MCP_SERVER_FILE = fileURLToPath(new URL(\"./mcp-server.ts\", import.meta.url));\nconst PACKAGED_MCP_SERVER_FILE = fileURLToPath(\n new URL(\"./runtime/cron/mcp-server.ts\", import.meta.url),\n);\n\nexport interface CronModuleOptions {\n pollIntervalMs?: number;\n runTimeoutMs?: number;\n queueTimeoutMs?: number;\n scriptTimeoutMs?: number;\n database?: CronDatabase;\n host?: Partial<CronHost>;\n mcpServerFile?: string;\n}\n\nfunction envMs(name: string, fallback: number): number {\n const parsed = Number.parseInt(process.env[name] ?? \"\", 10);\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;\n}\n\nexport function createCronModule(options: CronModuleOptions = {}): NegotiumNodeModule {\n return {\n name: \"cron\",\n singleton: true,\n capabilities: [\"scheduler.cron.v1\", \"scheduler.cron.v2\"],\n start(context) {\n const restoreDatabase = options.database ? configureCronDatabase(options.database) : () => {};\n const restoreHost = configureCronHost(options.host ?? {});\n const host = cronHost();\n ensureCronSchema();\n const cleanupTasks = new Set<Promise<unknown>>();\n const trackCleanup = (promise: Promise<unknown>) => {\n cleanupTasks.add(promise);\n void promise.then(\n () => cleanupTasks.delete(promise),\n (error) => {\n cleanupTasks.delete(promise);\n logger.warn({ err: error }, \"cron: topic context cleanup failed\");\n },\n );\n };\n for (const topicId of new Set(\n listOrphanedCronTopicSessions().map((entry) => entry.topicId),\n )) {\n trackCleanup(resetCronTopicContext(topicId));\n }\n const unsubscribeTopicCleanup = context.bus.subscribe((event) => {\n if (event.type === \"topic-deleted\") trackCleanup(resetCronTopicContext(event.topicId));\n });\n const mcpServerFile =\n options.mcpServerFile ??\n (existsSync(SOURCE_MCP_SERVER_FILE) ? SOURCE_MCP_SERVER_FILE : PACKAGED_MCP_SERVER_FILE);\n const unregisterMcp = host.registerRuntimeMcpServer(\"cron-manager\", {\n scopes: [\"forum\", \"manager\"],\n forumRequired: true,\n build({ userId, session, topicId, agent }) {\n const args = [`--user-id=${userId}`, `--topic=${session}`];\n if (topicId) args.push(`--topic-id=${topicId}`);\n if (host.authorize?.(userId, \"cron:admin\", { type: \"cron\" })) {\n args.push(\"--authorized=true\");\n }\n return host.buildStdioMcpServer(agent, mcpServerFile, args);\n },\n });\n const unregisterBackgroundSessions = host.registerBackgroundSessionProvider(\n listCronBackgroundSessions,\n );\n\n const schedulerOptions: CronSchedulerOptions = {\n bus: context.bus,\n pollIntervalMs: options.pollIntervalMs ?? envMs(\"NEGOTIUM_CRON_POLL_INTERVAL_MS\", 1_000),\n runTimeoutMs: options.runTimeoutMs ?? envMs(\"NEGOTIUM_CRON_RUN_TIMEOUT_MS\", 10 * 60_000),\n queueTimeoutMs:\n options.queueTimeoutMs ?? envMs(\"NEGOTIUM_CRON_QUEUE_TIMEOUT_MS\", 5 * 60_000),\n dispatch:\n options.host?.dispatch ??\n (async (job, run, hooks, execution) => {\n const taskPrompt =\n job.prompt ??\n (await runCronPromptScript({\n script: job.script!,\n cwd: host.resolveTopicWorkspaceDir(job.topicId),\n jobId: job.id,\n topicId: job.topicId,\n timeoutMs:\n options.scriptTimeoutMs ?? envMs(\"NEGOTIUM_CRON_SCRIPT_TIMEOUT_MS\", 10 * 60_000),\n signal: execution.signal,\n }));\n if (!taskPrompt.trim()) {\n return { status: \"skipped\", reason: \"prompt script produced no output\" } as const;\n }\n if (!host.getTopic(job.topicId)?.agent)\n throw new Error(\"target topic was deleted during run\");\n const prompt = [\n `<scheduled-task name=\"${job.name}\" run-id=\"${run.id}\">`,\n \"This is a background scheduled run. Do not ask interactive questions.\",\n \"All scheduled tasks in this topic share one Cron conversation.\",\n \"Use relevant conclusions and state from previous scheduled runs before acting.\",\n \"Complete the task and report only meaningful results in the user's language.\",\n taskPrompt,\n \"</scheduled-task>\",\n ].join(\"\\n\");\n const requestId = `cron:${run.id}`;\n const queryId = triggerTopicAiTurn(\n job.topicId,\n job.ownerUserId,\n prompt,\n execution.agent,\n {\n origin: `cron:${job.id}:${run.id}`,\n requestId,\n hideInjectMessage: true,\n modelOverride: job.model,\n effortOverride: job.effort,\n sessionId: execution.sessionId ?? null,\n sessionName: execution.sessionName,\n sessionType: \"cron\",\n onDispatched: hooks.onDispatched,\n onSessionId: hooks.onSessionId,\n onSessionReset: hooks.onSessionReset,\n bridgeSessionFromHistory: true,\n onSettled: hooks.onSettled,\n },\n );\n return {\n status: queryId ? (\"dispatched\" as const) : (\"deferred\" as const),\n requestId,\n ...(queryId ? { queryId } : {}),\n cancel: () => cancelDeferredInject(job.topicId, requestId),\n };\n }),\n };\n const scheduler = new CronScheduler(schedulerOptions);\n scheduler.start();\n\n return {\n async stop() {\n scheduler.stop();\n unsubscribeTopicCleanup();\n unregisterBackgroundSessions();\n unregisterMcp();\n await Promise.allSettled(cleanupTasks);\n restoreHost();\n restoreDatabase();\n },\n };\n },\n };\n}\n",
|
|
12
|
-
"import {\n type AgentKind,\n type AiTurnSettlement,\n logger,\n type MessageDto,\n type RuntimeBus,\n runtimeBus,\n} from \"@negotium/core\";\nimport { CRON_CONTEXT_ROTATE_EVERY, rotateCronTopicContext } from \"#context\";\nimport { cronHost } from \"#host\";\nimport {\n type CronJobRecord,\n type CronRunRecord,\n claimCronCancellations,\n claimCronRuns,\n clearCronTopicSession,\n cronTopicSessionName,\n finalizeOrphanedCronRuns,\n finishCronRun,\n getCronJob,\n getCronTopicContext,\n getCronTopicSession,\n markCronRunStarted,\n recoverPendingCronRuns,\n setCronJobEnabled,\n setCronTopicSessionIfJobUpdatedAt,\n} from \"#store\";\n\nexport interface CronDispatchHooks {\n onDispatched(queryId: string): void;\n onSessionId(sessionId: string): void;\n onSessionReset(): void;\n onSettled(result: AiTurnSettlement): void;\n}\n\nexport interface CronExecutionContext {\n agent: AgentKind;\n sessionId?: string;\n sessionName: string;\n signal: AbortSignal;\n}\n\nexport type CronDispatchResult =\n | string\n | null\n | { status: \"skipped\"; reason: string }\n | {\n status: \"deferred\" | \"dispatched\";\n requestId: string;\n queryId?: string;\n cancel(): boolean;\n };\n\nexport type CronDispatch = (\n job: CronJobRecord,\n run: CronRunRecord,\n hooks: CronDispatchHooks,\n context: CronExecutionContext,\n) => CronDispatchResult | Promise<CronDispatchResult>;\n\nexport interface CronSchedulerOptions {\n dispatch: CronDispatch;\n bus?: RuntimeBus;\n pollIntervalMs?: number;\n runTimeoutMs?: number;\n queueTimeoutMs?: number;\n now?: () => Date;\n}\n\ninterface ActiveRun {\n job: CronJobRecord;\n run: CronRunRecord;\n agent: AgentKind;\n queryId?: string;\n outputPreview?: string;\n timeout?: ReturnType<typeof setTimeout>;\n deferredTimeout?: ReturnType<typeof setTimeout>;\n cancelDeferred?: () => boolean;\n abortController: AbortController;\n}\n\ninterface QueuedRun {\n job: CronJobRecord;\n run: CronRunRecord;\n queuedAt: number;\n}\n\nconst DEFAULT_POLL_INTERVAL_MS = 1_000;\nconst DEFAULT_RUN_TIMEOUT_MS = 10 * 60_000;\nconst DEFAULT_QUEUE_TIMEOUT_MS = 5 * 60_000;\n\n/**\n * One durable scheduler per node process.\n *\n * SQLite owns due/manual requests. The in-memory layer only serializes work\n * per topic: every job attached to a topic observes the provider session and\n * provider-agnostic conversation log written by the previous topic Cron run.\n */\nexport class CronScheduler {\n readonly #dispatch: CronDispatch;\n readonly #bus: RuntimeBus;\n readonly #pollIntervalMs: number;\n readonly #runTimeoutMs: number;\n readonly #queueTimeoutMs: number;\n readonly #now: () => Date;\n readonly #activeByTopic = new Map<string, ActiveRun>();\n readonly #activeByQuery = new Map<string, ActiveRun>();\n readonly #queuedByTopic = new Map<string, QueuedRun[]>();\n readonly #maintenanceByTopic = new Set<string>();\n readonly #rotationBypassOnce = new Set<string>();\n #timer?: ReturnType<typeof setInterval>;\n #unsubscribe?: () => void;\n #ticking = false;\n #stopped = false;\n\n constructor(options: CronSchedulerOptions) {\n this.#dispatch = options.dispatch;\n this.#bus = options.bus ?? runtimeBus();\n this.#pollIntervalMs = Math.max(250, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);\n this.#runTimeoutMs = Math.max(1_000, options.runTimeoutMs ?? DEFAULT_RUN_TIMEOUT_MS);\n this.#queueTimeoutMs = Math.max(1_000, options.queueTimeoutMs ?? DEFAULT_QUEUE_TIMEOUT_MS);\n this.#now = options.now ?? (() => new Date());\n }\n\n start(): () => void {\n if (this.#timer) return () => this.stop();\n this.#stopped = false;\n const orphaned = finalizeOrphanedCronRuns(this.#now());\n if (orphaned > 0) logger.warn({ orphaned }, \"cron: finalized interrupted runs on startup\");\n this.#unsubscribe = this.#bus.subscribe((event) => {\n if (event.type !== \"message\") return;\n const message = event.payload as MessageDto;\n if (!message?.queryId || message.authorId !== \"ai\") return;\n const active = this.#activeByQuery.get(message.queryId);\n if (active) active.outputPreview = message.text.slice(0, 500);\n });\n this.#timer = setInterval(() => void this.tick(), this.#pollIntervalMs);\n this.#timer.unref?.();\n const recovered = recoverPendingCronRuns();\n for (const pending of recovered) this.#enqueueOrStart(pending.job, pending.run);\n if (recovered.length > 0) {\n logger.info({ recovered: recovered.length }, \"cron: recovered pending pre-dispatch runs\");\n }\n void this.tick();\n return () => this.stop();\n }\n\n stop(): void {\n const host = cronHost();\n if (this.#timer) clearInterval(this.#timer);\n this.#timer = undefined;\n this.#stopped = true;\n this.#unsubscribe?.();\n this.#unsubscribe = undefined;\n\n for (const active of [...this.#activeByTopic.values()]) {\n active.abortController.abort();\n active.cancelDeferred?.();\n if (active.queryId && host.getRoomQuery(active.job.topicId)?.queryId === active.queryId) {\n host.abortRoom(active.job.topicId);\n }\n this.#finish(active, \"aborted\", \"scheduler stopped before run completed\");\n }\n for (const queued of this.#queuedByTopic.values()) {\n for (const entry of queued) {\n finishCronRun(entry.run.id, {\n status: \"aborted\",\n error: \"scheduler stopped before queued run dispatched\",\n });\n }\n }\n this.#queuedByTopic.clear();\n this.#activeByTopic.clear();\n this.#activeByQuery.clear();\n this.#rotationBypassOnce.clear();\n }\n\n async tick(): Promise<void> {\n if (this.#ticking || this.#stopped) return;\n this.#ticking = true;\n try {\n for (const jobId of claimCronCancellations()) this.#cancelJob(jobId);\n for (const claimed of claimCronRuns(this.#now())) {\n this.#enqueueOrStart(claimed.job, claimed.run);\n }\n } catch (error) {\n logger.warn({ err: error }, \"cron: scheduler tick failed\");\n } finally {\n this.#ticking = false;\n }\n }\n\n #enqueueOrStart(job: CronJobRecord, run: CronRunRecord): void {\n const topic = cronHost().getTopic(job.topicId);\n if (!topic?.agent) {\n setCronJobEnabled(job.id, false, this.#now());\n finishCronRun(run.id, {\n status: \"failed\",\n error: \"target topic is missing or has no agent; job disabled\",\n });\n return;\n }\n if (!topic.participants.some((participant) => participant.userId === job.ownerUserId)) {\n setCronJobEnabled(job.id, false, this.#now());\n finishCronRun(run.id, {\n status: \"failed\",\n error: \"job owner is no longer a topic participant; job disabled\",\n });\n return;\n }\n\n if (this.#activeByTopic.has(job.topicId) || this.#maintenanceByTopic.has(job.topicId)) {\n const queue = this.#queuedByTopic.get(job.topicId) ?? [];\n queue.push({ job, run, queuedAt: this.#now().getTime() });\n this.#queuedByTopic.set(job.topicId, queue);\n return;\n }\n const rotationDue =\n (getCronTopicContext(job.topicId)?.successfulRunsSinceRotation ?? 0) >=\n CRON_CONTEXT_ROTATE_EVERY;\n if (rotationDue && !this.#rotationBypassOnce.delete(job.topicId)) {\n const queue = this.#queuedByTopic.get(job.topicId) ?? [];\n queue.push({ job, run, queuedAt: this.#now().getTime() });\n this.#queuedByTopic.set(job.topicId, queue);\n this.#beginContextRotation(job.topicId);\n return;\n }\n this.#start(job, run, job.agent ?? topic.agent);\n }\n\n #start(job: CronJobRecord, run: CronRunRecord, agent: AgentKind): void {\n const storedSession = getCronTopicSession(job.topicId, agent);\n if (storedSession && storedSession.ownerUserId !== job.ownerUserId) {\n setCronJobEnabled(job.id, false, this.#now());\n finishCronRun(run.id, {\n status: \"failed\",\n error: \"topic Cron context belongs to another owner; job disabled\",\n });\n this.#drainTopic(job.topicId);\n return;\n }\n const active: ActiveRun = { job, run, agent, abortController: new AbortController() };\n this.#activeByTopic.set(job.topicId, active);\n const hooks: CronDispatchHooks = {\n onDispatched: (queryId) => this.#markDispatched(active, queryId),\n onSessionId: (sessionId) => {\n if (this.#isActive(active)) {\n setCronTopicSessionIfJobUpdatedAt(\n job.id,\n job.updatedAt,\n job.topicId,\n agent,\n job.ownerUserId,\n sessionId,\n this.#now(),\n );\n }\n },\n onSessionReset: () => {\n if (this.#isActive(active) && getCronJob(job.id)?.updatedAt === job.updatedAt) {\n clearCronTopicSession(job.topicId, agent);\n }\n },\n onSettled: (result) => this.#settle(active, result),\n };\n const context: CronExecutionContext = {\n agent,\n sessionId: storedSession?.sessionId,\n sessionName: cronTopicSessionName(job.topicId),\n signal: active.abortController.signal,\n };\n\n Promise.resolve()\n .then(() => this.#dispatch(job, run, hooks, context))\n .then((result) => this.#handleDispatchResult(active, result))\n .catch((error) => {\n this.#finish(active, \"failed\", error instanceof Error ? error.message : String(error));\n });\n }\n\n #handleDispatchResult(active: ActiveRun, result: CronDispatchResult): void {\n if (!this.#isActive(active)) return;\n if (typeof result === \"string\") {\n if (active.queryId !== result) this.#markDispatched(active, result);\n return;\n }\n if (result === null) {\n this.#finish(active, \"failed\", \"dispatch returned no query and no deferred handle\");\n return;\n }\n if (result.status === \"skipped\") {\n this.#finish(active, \"skipped\", result.reason);\n return;\n }\n\n active.cancelDeferred = result.cancel;\n if (result.status === \"dispatched\") {\n if (result.queryId && active.queryId !== result.queryId) {\n this.#markDispatched(active, result.queryId);\n }\n return;\n }\n active.deferredTimeout = setTimeout(() => {\n if (!this.#isActive(active) || active.queryId) return;\n const cancelled = active.cancelDeferred?.() ?? false;\n this.#finish(\n active,\n cancelled ? \"skipped\" : \"failed\",\n cancelled\n ? `topic remained busy for more than ${this.#queueTimeoutMs}ms`\n : \"deferred run disappeared before dispatch\",\n );\n }, this.#queueTimeoutMs);\n active.deferredTimeout.unref?.();\n }\n\n #markDispatched(active: ActiveRun, queryId: string): void {\n if (!this.#isActive(active)) return;\n if (active.queryId) this.#activeByQuery.delete(active.queryId);\n if (active.timeout) clearTimeout(active.timeout);\n if (active.deferredTimeout) clearTimeout(active.deferredTimeout);\n active.deferredTimeout = undefined;\n active.queryId = queryId;\n this.#activeByQuery.set(queryId, active);\n markCronRunStarted(active.run.id, queryId, this.#now());\n active.timeout = setTimeout(() => {\n const host = cronHost();\n const room = host.getRoomQuery(active.job.topicId);\n if (room?.queryId === active.queryId) host.abortRoom(active.job.topicId);\n else active.cancelDeferred?.();\n this.#finish(active, \"failed\", `run exceeded ${this.#runTimeoutMs}ms`);\n }, this.#runTimeoutMs);\n active.timeout.unref?.();\n }\n\n #settle(active: ActiveRun, result: AiTurnSettlement): void {\n if (!this.#isActive(active)) return;\n if (result.queryId && active.queryId && result.queryId !== active.queryId) return;\n if (result.kind === \"completed\") this.#finish(active, \"succeeded\");\n else if (result.kind === \"aborted\") this.#finish(active, \"aborted\", result.error);\n else this.#finish(active, \"failed\", result.error ?? \"agent turn failed\");\n }\n\n #finish(\n active: ActiveRun,\n status: \"succeeded\" | \"failed\" | \"aborted\" | \"skipped\",\n error?: string,\n ): void {\n if (!this.#isActive(active)) return;\n active.abortController.abort();\n if (active.timeout) clearTimeout(active.timeout);\n if (active.deferredTimeout) clearTimeout(active.deferredTimeout);\n if (active.queryId) this.#activeByQuery.delete(active.queryId);\n this.#activeByTopic.delete(active.job.topicId);\n const successfulRunsSinceRotation = finishCronRun(\n active.run.id,\n { status, outputPreview: active.outputPreview, error },\n this.#now(),\n );\n if (\n status === \"succeeded\" &&\n successfulRunsSinceRotation !== null &&\n successfulRunsSinceRotation >= CRON_CONTEXT_ROTATE_EVERY\n ) {\n this.#beginContextRotation(active.job.topicId);\n return;\n }\n if (!this.#stopped) queueMicrotask(() => this.#drainTopic(active.job.topicId));\n }\n\n #beginContextRotation(topicId: string): void {\n if (this.#stopped || this.#maintenanceByTopic.has(topicId)) return;\n this.#maintenanceByTopic.add(topicId);\n void rotateCronTopicContext(topicId)\n .then((result) => {\n if (!result.rotated) {\n this.#rotationBypassOnce.add(topicId);\n logger.warn({ topicId }, \"cron: context rotation deferred after cleanup failure\");\n }\n })\n .catch((error) => {\n this.#rotationBypassOnce.add(topicId);\n logger.warn({ err: error, topicId }, \"cron: context rotation failed\");\n })\n .finally(() => {\n this.#maintenanceByTopic.delete(topicId);\n if (!this.#stopped) this.#drainTopic(topicId);\n });\n }\n\n #drainTopic(topicId: string): void {\n if (this.#stopped || this.#activeByTopic.has(topicId) || this.#maintenanceByTopic.has(topicId))\n return;\n const queue = this.#queuedByTopic.get(topicId);\n while (queue?.length) {\n const next = queue.shift()!;\n if (this.#now().getTime() - next.queuedAt > this.#queueTimeoutMs) {\n finishCronRun(next.run.id, {\n status: \"skipped\",\n error: `topic Cron queue wait exceeded ${this.#queueTimeoutMs}ms`,\n });\n continue;\n }\n const currentJob = getCronJob(next.job.id);\n if (!currentJob) continue;\n if (queue.length === 0) this.#queuedByTopic.delete(topicId);\n this.#enqueueOrStart(currentJob, next.run);\n return;\n }\n this.#queuedByTopic.delete(topicId);\n }\n\n #cancelJob(jobId: string): void {\n for (const active of [...this.#activeByTopic.values()]) {\n if (active.job.id !== jobId) continue;\n active.abortController.abort();\n active.cancelDeferred?.();\n const host = cronHost();\n if (active.queryId && host.getRoomQuery(active.job.topicId)?.queryId === active.queryId) {\n host.abortRoom(active.job.topicId);\n }\n this.#finish(active, \"aborted\", \"run cancelled by cron_kill\");\n }\n for (const [topicId, queue] of this.#queuedByTopic) {\n const kept: QueuedRun[] = [];\n for (const queued of queue) {\n if (queued.job.id === jobId) {\n finishCronRun(queued.run.id, {\n status: \"aborted\",\n error: \"queued run cancelled by cron_kill\",\n });\n } else {\n kept.push(queued);\n }\n }\n if (kept.length > 0) this.#queuedByTopic.set(topicId, kept);\n else this.#queuedByTopic.delete(topicId);\n }\n }\n\n #isActive(active: ActiveRun): boolean {\n return this.#activeByTopic.get(active.job.topicId) === active;\n }\n}\n",
|
|
13
|
-
"import { logger } from \"@negotium/core\";\nimport { updateCronJobSummaryIfPromptMatches } from \"#store\";\n\nconst DEFAULT_URL = \"https://api.deepseek.com/v1/chat/completions\";\nconst DEFAULT_MODEL = \"deepseek-chat\";\nconst MAX_SUMMARY_LENGTH = 60;\nconst MAX_CONCURRENT_SUMMARIES = 3;\n\nexport type CronPromptSummarizer = (prompt: string) => Promise<string | null>;\n\nexport function cleanCronPromptSummary(raw: string): string | null {\n let text = raw.replace(/\\s+/g, \" \").trim();\n text = text.replace(/^[\"'“”]+|[\"'“”]+$/g, \"\").trim();\n text = text.replace(/^(task|summary|label|요약|작업)\\s*[::]\\s*/i, \"\").trim();\n text = text.replace(/^[\"'“”]+|[\"'“”]+$/g, \"\").trim();\n text = text.replace(/[.。]+$/, \"\").trim();\n if (!text) return null;\n return text.length > MAX_SUMMARY_LENGTH ? `${text.slice(0, MAX_SUMMARY_LENGTH - 3)}...` : text;\n}\n\nexport async function summarizeCronPrompt(prompt: string): Promise<string | null> {\n const clean = prompt.trim();\n const apiKey = process.env.NEGOTIUM_CRON_SUMMARY_API_KEY ?? process.env.DEEPSEEK_API_KEY;\n if (!clean || !apiKey) return null;\n\n const controller = new AbortController();\n const timeoutMs = Number.parseInt(process.env.NEGOTIUM_CRON_SUMMARY_TIMEOUT_MS ?? \"8000\", 10);\n const timer = setTimeout(\n () => controller.abort(),\n Number.isFinite(timeoutMs) ? timeoutMs : 8_000,\n );\n try {\n const response = await fetch(process.env.NEGOTIUM_CRON_SUMMARY_URL ?? DEFAULT_URL, {\n method: \"POST\",\n signal: controller.signal,\n headers: { \"Content-Type\": \"application/json\", Authorization: `Bearer ${apiKey}` },\n body: JSON.stringify({\n model: process.env.NEGOTIUM_CRON_SUMMARY_MODEL ?? DEFAULT_MODEL,\n messages: [\n {\n role: \"system\",\n content:\n \"Label the scheduled task in the instruction's language using at most 8 words. \" +\n \"Describe the outcome only. Return no quotes, prefix, or trailing period.\",\n },\n { role: \"user\", content: clean.slice(0, 4_000) },\n ],\n max_tokens: 512,\n temperature: 0.2,\n stream: false,\n }),\n });\n if (!response.ok) {\n logger.warn({ status: response.status }, \"cron: prompt summary request failed\");\n return null;\n }\n const body = (await response.json()) as { choices?: Array<{ message?: { content?: string } }> };\n return cleanCronPromptSummary(body.choices?.[0]?.message?.content ?? \"\");\n } catch (error) {\n logger.warn({ err: error }, \"cron: prompt summary request errored\");\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\ninterface PendingSummary {\n jobId: string;\n prompt: string;\n generation: number;\n summarize: CronPromptSummarizer;\n}\n\nconst generations = new Map<string, number>();\nconst pending: PendingSummary[] = [];\nlet active = 0;\nlet nextGeneration = 0;\n\nfunction drain(): void {\n while (active < MAX_CONCURRENT_SUMMARIES) {\n const next = pending.shift();\n if (!next) return;\n if (generations.get(next.jobId) !== next.generation) continue;\n active += 1;\n void next\n .summarize(next.prompt)\n .then((summary) => {\n if (summary && generations.get(next.jobId) === next.generation) {\n updateCronJobSummaryIfPromptMatches(next.jobId, next.prompt, summary);\n }\n })\n .catch((error) =>\n logger.warn({ err: error, jobId: next.jobId }, \"cron: summary persist failed\"),\n )\n .finally(() => {\n active -= 1;\n if (generations.get(next.jobId) === next.generation) generations.delete(next.jobId);\n drain();\n });\n }\n}\n\nexport function queueCronPromptSummary(\n jobId: string,\n prompt: string,\n summarize: CronPromptSummarizer = summarizeCronPrompt,\n): void {\n const generation = ++nextGeneration;\n generations.set(jobId, generation);\n for (let index = pending.length - 1; index >= 0; index -= 1) {\n if (pending[index]?.jobId === jobId) pending.splice(index, 1);\n }\n pending.push({ jobId, prompt, generation, summarize });\n drain();\n}\n"
|
|
14
|
-
],
|
|
15
|
-
"mappings": "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",
|
|
16
|
-
"debugId": "FE44C8C344157EBA64756E2164756E21",
|
|
17
|
-
"names": []
|
|
18
|
-
}
|
package/dist/chunk-59d0shyj.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import {
|
|
3
|
-
PENDING_ASK_TTL_MS
|
|
4
|
-
} from "./chunk-rbvctv1n.js";
|
|
5
|
-
import {
|
|
6
|
-
deliverPeerReply
|
|
7
|
-
} from "./chunk-1k7t7bnx.js";
|
|
8
|
-
import"./chunk-pg1azh6n.js";
|
|
9
|
-
import {
|
|
10
|
-
storageDatabase
|
|
11
|
-
} from "./chunk-1a6kv9vt.js";
|
|
12
|
-
import"./chunk-f5xgprg1.js";
|
|
13
|
-
import"./chunk-hwy3n31w.js";
|
|
14
|
-
|
|
15
|
-
// ../../packages/core/src/runtime/ask-callbacks.ts
|
|
16
|
-
storageDatabase.exec(`
|
|
17
|
-
CREATE TABLE IF NOT EXISTS remote_ask_callbacks (
|
|
18
|
-
target_query_id TEXT PRIMARY KEY,
|
|
19
|
-
request_id TEXT NOT NULL,
|
|
20
|
-
node_name TEXT NOT NULL,
|
|
21
|
-
node_cell_id TEXT NOT NULL,
|
|
22
|
-
topic_id TEXT NOT NULL,
|
|
23
|
-
user_id TEXT NOT NULL,
|
|
24
|
-
created_at INTEGER NOT NULL
|
|
25
|
-
)
|
|
26
|
-
`);
|
|
27
|
-
var pendingAsks = new Map;
|
|
28
|
-
var MAX_ASK_AGE_MS = PENDING_ASK_TTL_MS;
|
|
29
|
-
function registerAskCallback(entry) {
|
|
30
|
-
pendingAsks.set(entry.targetQueryId, entry);
|
|
31
|
-
if (entry.remoteReply) {
|
|
32
|
-
storageDatabase.query(`INSERT INTO remote_ask_callbacks
|
|
33
|
-
(target_query_id, request_id, node_name, node_cell_id, topic_id, user_id, created_at)
|
|
34
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
35
|
-
ON CONFLICT(target_query_id) DO UPDATE SET
|
|
36
|
-
request_id = excluded.request_id,
|
|
37
|
-
node_name = excluded.node_name,
|
|
38
|
-
node_cell_id = excluded.node_cell_id,
|
|
39
|
-
topic_id = excluded.topic_id,
|
|
40
|
-
user_id = excluded.user_id,
|
|
41
|
-
created_at = excluded.created_at`).run(entry.targetQueryId, entry.remoteReply.requestId, entry.remoteReply.nodeName, entry.remoteReply.nodeCellId, entry.remoteReply.topicId, entry.remoteReply.userId, entry.createdAt);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function resolveAskCallback(queryId) {
|
|
45
|
-
const entry = pendingAsks.get(queryId);
|
|
46
|
-
if (!entry)
|
|
47
|
-
return null;
|
|
48
|
-
if (Date.now() - entry.createdAt > MAX_ASK_AGE_MS) {
|
|
49
|
-
entry.timedOut = true;
|
|
50
|
-
}
|
|
51
|
-
pendingAsks.delete(queryId);
|
|
52
|
-
storageDatabase.query("DELETE FROM remote_ask_callbacks WHERE target_query_id = ?").run(queryId);
|
|
53
|
-
return entry;
|
|
54
|
-
}
|
|
55
|
-
function purgeStaleAsks(now = Date.now()) {
|
|
56
|
-
for (const [id, entry] of pendingAsks) {
|
|
57
|
-
if (now - entry.createdAt > MAX_ASK_AGE_MS)
|
|
58
|
-
pendingAsks.delete(id);
|
|
59
|
-
}
|
|
60
|
-
storageDatabase.query("DELETE FROM remote_ask_callbacks WHERE created_at < ?").run(now - MAX_ASK_AGE_MS);
|
|
61
|
-
}
|
|
62
|
-
function cancelAskCallbacksForTopic(topicId) {
|
|
63
|
-
let cancelled = 0;
|
|
64
|
-
for (const [queryId, entry] of pendingAsks) {
|
|
65
|
-
if (entry.callerTopicId !== topicId)
|
|
66
|
-
continue;
|
|
67
|
-
pendingAsks.delete(queryId);
|
|
68
|
-
storageDatabase.query("DELETE FROM remote_ask_callbacks WHERE target_query_id = ?").run(queryId);
|
|
69
|
-
cancelled++;
|
|
70
|
-
}
|
|
71
|
-
return cancelled;
|
|
72
|
-
}
|
|
73
|
-
async function failInterruptedRemoteAskCallbacks() {
|
|
74
|
-
const rows = storageDatabase.query(`SELECT target_query_id, request_id, node_name, node_cell_id, topic_id, user_id
|
|
75
|
-
FROM remote_ask_callbacks
|
|
76
|
-
ORDER BY created_at ASC`).all();
|
|
77
|
-
let failed = 0;
|
|
78
|
-
for (const row of rows) {
|
|
79
|
-
const delivered = await deliverPeerReply({
|
|
80
|
-
nodeName: row.node_name,
|
|
81
|
-
nodeCellId: row.node_cell_id,
|
|
82
|
-
topicId: row.topic_id,
|
|
83
|
-
userId: row.user_id,
|
|
84
|
-
requestId: row.request_id
|
|
85
|
-
}, "peer", "The remote worker restarted before this ask completed. Please retry the request.", "error");
|
|
86
|
-
if (!delivered)
|
|
87
|
-
continue;
|
|
88
|
-
storageDatabase.query("DELETE FROM remote_ask_callbacks WHERE target_query_id = ?").run(row.target_query_id);
|
|
89
|
-
failed++;
|
|
90
|
-
}
|
|
91
|
-
return failed;
|
|
92
|
-
}
|
|
93
|
-
export {
|
|
94
|
-
resolveAskCallback,
|
|
95
|
-
registerAskCallback,
|
|
96
|
-
purgeStaleAsks,
|
|
97
|
-
failInterruptedRemoteAskCallbacks,
|
|
98
|
-
cancelAskCallbacksForTopic
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
export { registerAskCallback, resolveAskCallback, cancelAskCallbacksForTopic, failInterruptedRemoteAskCallbacks };
|
|
102
|
-
|
|
103
|
-
//# debugId=C582BFA9F68D79B564756E2164756E21
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../packages/core/src/runtime/ask-callbacks.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"/**\n * Ask callback registry (requestId → caller topic + cleanup).\n *\n * ask_session registers a callback here after the target AI turn is\n * dispatched; when the target AI completes, the turn runner resolves it and\n * routes the result back to the caller topic automatically.\n *\n * Ported from the in-memory registry half of otium's `api/routes/sessions.ts`\n * (the ask/tell REST routes stayed in the host).\n */\n\nimport { deliverPeerReply, type RemoteReplyRoute } from \"#mcp/session-comm/peer-forward\";\nimport { db } from \"#storage/forum-db\";\nimport { PENDING_ASK_TTL_MS, type PendingAskUserId } from \"#storage/session-asks\";\n\ndb.exec(`\n CREATE TABLE IF NOT EXISTS remote_ask_callbacks (\n target_query_id TEXT PRIMARY KEY,\n request_id TEXT NOT NULL,\n node_name TEXT NOT NULL,\n node_cell_id TEXT NOT NULL,\n topic_id TEXT NOT NULL,\n user_id TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )\n`);\n\ninterface PendingAskIdentity {\n userId: PendingAskUserId;\n from: string;\n to: string;\n requestId?: string;\n}\n\nexport interface AskPending {\n requestId: string;\n contextId?: string;\n callerTopicId: string;\n callerUserId: string;\n targetQueryId: string;\n createdAt: number;\n timedOut?: boolean; // set when resolved past TTL — caller gets timeout notice\n pendingAsk?: PendingAskIdentity;\n remoteReply?: RemoteReplyRoute;\n}\n\n/** Active callbacks stay in memory for the normal completion path. Remote\n * reply routes are also persisted so a process restart can explicitly fail\n * the interrupted ask instead of leaving its caller waiting forever. */\nconst pendingAsks = new Map<string, AskPending>(); // keyed by targetQueryId (the AI turn's queryId)\nconst MAX_ASK_AGE_MS = PENDING_ASK_TTL_MS;\n\n/** Register an ask callback. Called after the target AI turn is dispatched. */\nexport function registerAskCallback(entry: AskPending): void {\n pendingAsks.set(entry.targetQueryId, entry);\n if (entry.remoteReply) {\n db.query(\n `INSERT INTO remote_ask_callbacks\n (target_query_id, request_id, node_name, node_cell_id, topic_id, user_id, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(target_query_id) DO UPDATE SET\n request_id = excluded.request_id,\n node_name = excluded.node_name,\n node_cell_id = excluded.node_cell_id,\n topic_id = excluded.topic_id,\n user_id = excluded.user_id,\n created_at = excluded.created_at`,\n ).run(\n entry.targetQueryId,\n entry.remoteReply.requestId,\n entry.remoteReply.nodeName,\n entry.remoteReply.nodeCellId,\n entry.remoteReply.topicId,\n entry.remoteReply.userId,\n entry.createdAt,\n );\n }\n}\n\n/** Resolve an ask callback after the target AI finishes.\n * Called from the turn runner when the agent stream ends — matches by the AI\n * turn's queryId. Returns the AskPending or null if not found.\n * Past TTL: returns entry with timedOut=true (caller gets timeout notice). */\nexport function resolveAskCallback(queryId: string): AskPending | null {\n const entry = pendingAsks.get(queryId);\n if (!entry) return null;\n if (Date.now() - entry.createdAt > MAX_ASK_AGE_MS) {\n entry.timedOut = true;\n }\n pendingAsks.delete(queryId);\n db.query(\"DELETE FROM remote_ask_callbacks WHERE target_query_id = ?\").run(queryId);\n return entry;\n}\n\n/** Clean up stale asks that outlived their TTL (periodic, lightweight). */\nexport function purgeStaleAsks(now: number = Date.now()): void {\n for (const [id, entry] of pendingAsks) {\n if (now - entry.createdAt > MAX_ASK_AGE_MS) pendingAsks.delete(id);\n }\n db.query(\"DELETE FROM remote_ask_callbacks WHERE created_at < ?\").run(now - MAX_ASK_AGE_MS);\n}\n\n/** Drop callbacks whose caller topic was hard-deleted. */\nexport function cancelAskCallbacksForTopic(topicId: string): number {\n let cancelled = 0;\n for (const [queryId, entry] of pendingAsks) {\n if (entry.callerTopicId !== topicId) continue;\n pendingAsks.delete(queryId);\n db.query(\"DELETE FROM remote_ask_callbacks WHERE target_query_id = ?\").run(queryId);\n cancelled++;\n }\n return cancelled;\n}\n\ninterface DurableRemoteAskCallbackRow {\n target_query_id: string;\n request_id: string;\n node_name: string;\n node_cell_id: string;\n topic_id: string;\n user_id: string;\n}\n\n/** Fail remote asks whose target turn was interrupted by the previous process.\n * Call once during startup after the peer session bridge is registered. */\nexport async function failInterruptedRemoteAskCallbacks(): Promise<number> {\n const rows = db\n .query(\n `SELECT target_query_id, request_id, node_name, node_cell_id, topic_id, user_id\n FROM remote_ask_callbacks\n ORDER BY created_at ASC`,\n )\n .all() as DurableRemoteAskCallbackRow[];\n let failed = 0;\n for (const row of rows) {\n const delivered = await deliverPeerReply(\n {\n nodeName: row.node_name,\n nodeCellId: row.node_cell_id,\n topicId: row.topic_id,\n userId: row.user_id,\n requestId: row.request_id,\n },\n \"peer\",\n \"The remote worker restarted before this ask completed. Please retry the request.\",\n \"error\",\n );\n if (!delivered) continue;\n db.query(\"DELETE FROM remote_ask_callbacks WHERE target_query_id = ?\").run(row.target_query_id);\n failed++;\n }\n return failed;\n}\n"
|
|
6
|
-
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;AAeA,gBAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAUP;AAwBD,IAAM,cAAc,IAAI;AACxB,IAAM,iBAAiB;AAGhB,SAAS,mBAAmB,CAAC,OAAyB;AAAA,EAC3D,YAAY,IAAI,MAAM,eAAe,KAAK;AAAA,EAC1C,IAAI,MAAM,aAAa;AAAA,IACrB,gBAAG,MACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CAUF,EAAE,IACA,MAAM,eACN,MAAM,YAAY,WAClB,MAAM,YAAY,UAClB,MAAM,YAAY,YAClB,MAAM,YAAY,SAClB,MAAM,YAAY,QAClB,MAAM,SACR;AAAA,EACF;AAAA;AAOK,SAAS,kBAAkB,CAAC,SAAoC;AAAA,EACrE,MAAM,QAAQ,YAAY,IAAI,OAAO;AAAA,EACrC,KAAK;AAAA,IAAO,OAAO;AAAA,EACnB,IAAI,KAAK,IAAI,IAAI,MAAM,YAAY,gBAAgB;AAAA,IACjD,MAAM,WAAW;AAAA,EACnB;AAAA,EACA,YAAY,OAAO,OAAO;AAAA,EAC1B,gBAAG,MAAM,4DAA4D,EAAE,IAAI,OAAO;AAAA,EAClF,OAAO;AAAA;AAIF,SAAS,cAAc,CAAC,MAAc,KAAK,IAAI,GAAS;AAAA,EAC7D,YAAY,IAAI,UAAU,aAAa;AAAA,IACrC,IAAI,MAAM,MAAM,YAAY;AAAA,MAAgB,YAAY,OAAO,EAAE;AAAA,EACnE;AAAA,EACA,gBAAG,MAAM,uDAAuD,EAAE,IAAI,MAAM,cAAc;AAAA;AAIrF,SAAS,0BAA0B,CAAC,SAAyB;AAAA,EAClE,IAAI,YAAY;AAAA,EAChB,YAAY,SAAS,UAAU,aAAa;AAAA,IAC1C,IAAI,MAAM,kBAAkB;AAAA,MAAS;AAAA,IACrC,YAAY,OAAO,OAAO;AAAA,IAC1B,gBAAG,MAAM,4DAA4D,EAAE,IAAI,OAAO;AAAA,IAClF;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAcT,eAAsB,iCAAiC,GAAoB;AAAA,EACzE,MAAM,OAAO,gBACV,MACC;AAAA;AAAA,+BAGF,EACC,IAAI;AAAA,EACP,IAAI,SAAS;AAAA,EACb,WAAW,OAAO,MAAM;AAAA,IACtB,MAAM,YAAY,MAAM,iBACtB;AAAA,MACE,UAAU,IAAI;AAAA,MACd,YAAY,IAAI;AAAA,MAChB,SAAS,IAAI;AAAA,MACb,QAAQ,IAAI;AAAA,MACZ,WAAW,IAAI;AAAA,IACjB,GACA,QACA,oFACA,OACF;AAAA,IACA,KAAK;AAAA,MAAW;AAAA,IAChB,gBAAG,MAAM,4DAA4D,EAAE,IAAI,IAAI,eAAe;AAAA,IAC9F;AAAA,EACF;AAAA,EACA,OAAO;AAAA;",
|
|
8
|
-
"debugId": "C582BFA9F68D79B564756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|