@coffer-org/server 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/auth-api.js +4 -5
  2. package/dist/cache-clock.d.ts +8 -0
  3. package/dist/cache-clock.js +103 -0
  4. package/dist/cache-refresh.d.ts +7 -0
  5. package/dist/cache-refresh.js +322 -0
  6. package/dist/collection-io.d.ts +13 -0
  7. package/dist/collection-io.js +40 -29
  8. package/dist/compute-unit.d.ts +66 -0
  9. package/dist/compute-unit.js +369 -0
  10. package/dist/condition-where.js +1 -3
  11. package/dist/data-dir.d.ts +3 -0
  12. package/dist/data-dir.js +39 -0
  13. package/dist/entity-schema.js +5 -2
  14. package/dist/extend-table.d.ts +1 -1
  15. package/dist/extend-table.js +22 -8
  16. package/dist/file-fields.js +50 -23
  17. package/dist/global-search.js +1 -1
  18. package/dist/index.js +20 -26
  19. package/dist/mcp-contract/client.d.ts +24 -0
  20. package/dist/mcp-contract/client.js +12 -0
  21. package/dist/mcp-contract/schema.d.ts +46 -0
  22. package/dist/mcp-contract/schema.js +90 -0
  23. package/dist/mcp-contract/tools.d.ts +19 -0
  24. package/dist/mcp-contract/tools.js +160 -0
  25. package/dist/mcp-local.d.ts +1 -1
  26. package/dist/mcp-local.js +1 -1
  27. package/dist/mcp-tools.d.ts +12 -1
  28. package/dist/mcp-tools.js +41 -8
  29. package/dist/migrations.d.ts +5 -0
  30. package/dist/migrations.js +122 -0
  31. package/dist/mutate.js +77 -36
  32. package/dist/orchestrator/agent-capabilities.d.ts +2 -0
  33. package/dist/orchestrator/agent-capabilities.js +201 -0
  34. package/dist/orchestrator/allow.d.ts +16 -0
  35. package/dist/orchestrator/allow.js +65 -0
  36. package/dist/orchestrator/attachments.d.ts +2 -0
  37. package/dist/orchestrator/attachments.js +11 -0
  38. package/dist/orchestrator/config.d.ts +4 -0
  39. package/dist/orchestrator/config.js +19 -0
  40. package/dist/orchestrator/db.d.ts +17 -0
  41. package/dist/orchestrator/db.js +12 -0
  42. package/dist/orchestrator/diagnostics.d.ts +9 -0
  43. package/dist/orchestrator/diagnostics.js +13 -0
  44. package/dist/orchestrator/environment.d.ts +3 -0
  45. package/dist/orchestrator/environment.js +30 -0
  46. package/dist/orchestrator/file-inspection.d.ts +3 -0
  47. package/dist/orchestrator/file-inspection.js +61 -0
  48. package/dist/orchestrator/format.d.ts +1 -0
  49. package/dist/orchestrator/format.js +6 -0
  50. package/dist/orchestrator/index.d.ts +30 -0
  51. package/dist/orchestrator/index.js +56 -0
  52. package/dist/orchestrator/live-message.d.ts +14 -0
  53. package/dist/orchestrator/live-message.js +100 -0
  54. package/dist/orchestrator/pipeline.d.ts +11 -0
  55. package/dist/orchestrator/pipeline.js +198 -0
  56. package/dist/orchestrator/registry.d.ts +13 -0
  57. package/dist/orchestrator/registry.js +74 -0
  58. package/dist/orchestrator/starters.d.ts +19 -0
  59. package/dist/orchestrator/starters.js +81 -0
  60. package/dist/orchestrator/suggestion-capabilities.d.ts +5 -0
  61. package/dist/orchestrator/suggestion-capabilities.js +33 -0
  62. package/dist/orchestrator/system-assembly.d.ts +7 -0
  63. package/dist/orchestrator/system-assembly.js +13 -0
  64. package/dist/orchestrator/system-capabilities.d.ts +2 -0
  65. package/dist/orchestrator/system-capabilities.js +63 -0
  66. package/dist/orchestrator/types.d.ts +144 -0
  67. package/dist/orchestrator/types.js +1 -0
  68. package/dist/part-injection.d.ts +3 -4
  69. package/dist/part-injection.js +25 -368
  70. package/dist/plugin-hooks.d.ts +1 -0
  71. package/dist/plugin-i18n.js +2 -1
  72. package/dist/plugin-runtime.d.ts +2 -2
  73. package/dist/plugin-runtime.js +20 -11
  74. package/dist/plugin-updates.d.ts +2 -2
  75. package/dist/plugin-updates.js +6 -12
  76. package/dist/public-url.js +2 -1
  77. package/dist/recompute-derived.d.ts +2 -1
  78. package/dist/recompute-derived.js +111 -34
  79. package/dist/records-api.js +14 -16
  80. package/dist/registry-context.d.ts +1 -1
  81. package/dist/registry-context.js +3 -0
  82. package/dist/search-index.js +1 -1
  83. package/dist/search-indexer.d.ts +1 -1
  84. package/dist/search-indexer.js +3 -3
  85. package/dist/settings-groups.d.ts +14 -0
  86. package/dist/settings-groups.js +25 -0
  87. package/dist/settings-write.d.ts +4 -4
  88. package/dist/settings-write.js +23 -26
  89. package/dist/system-settings.d.ts +3 -0
  90. package/dist/system-settings.js +27 -0
  91. package/dist/thread-store.d.ts +3 -2
  92. package/dist/thread-store.js +6 -5
  93. package/dist/trigger-queue.d.ts +33 -0
  94. package/dist/trigger-queue.js +432 -0
  95. package/dist/uploads.js +6 -3
  96. package/dist/value-compare.d.ts +1 -0
  97. package/dist/value-compare.js +7 -0
  98. package/package.json +17 -3
@@ -0,0 +1,16 @@
1
+ import type { GatePolicy } from './types.ts';
2
+ export declare const stateDir: () => string;
3
+ export declare function carryLegacyState(): void;
4
+ export declare function allowFileFor(connectorId: string): string;
5
+ export interface AllowState {
6
+ ids: Record<string, number>;
7
+ }
8
+ export declare function emptyAllowed(): AllowState;
9
+ export declare function loadAllowed(file: string): AllowState;
10
+ export declare function saveAllowed(file: string, state: AllowState): void;
11
+ export declare function isAllowed(state: AllowState, id: string | number | null | undefined): boolean;
12
+ export declare function addAllowed(state: AllowState, id: string | number, now: number): AllowState;
13
+ export declare function isSenderAllowed(connectorId: string, senderId: string, deps?: {
14
+ policy?: GatePolicy;
15
+ }): Promise<boolean>;
16
+ export declare function makeThrottle(maxAttempts?: number, windowMs?: number): (id: string | number, now?: number) => boolean;
@@ -0,0 +1,65 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { loadGatePolicy } from "./config.js";
4
+ import { carryInto, dataDir } from "../data-dir.js";
5
+ export const stateDir = () => process.env['ORCHESTRATOR_STATE_DIR'] ?? path.join(dataDir(), 'state');
6
+ function legacyStateDirs() {
7
+ return [
8
+ path.join(process.cwd(), 'packages', 'plugin-orchestrator', 'runtime', 'state'),
9
+ path.join(process.cwd(), 'node_modules', '@coffer-org', 'plugin-orchestrator', 'runtime', 'state'),
10
+ ];
11
+ }
12
+ export function carryLegacyState() {
13
+ carryInto(stateDir(), ...legacyStateDirs());
14
+ }
15
+ export function allowFileFor(connectorId) {
16
+ return path.join(stateDir(), `${connectorId}.allowed.json`);
17
+ }
18
+ export function emptyAllowed() {
19
+ return { ids: {} };
20
+ }
21
+ export function loadAllowed(file) {
22
+ try {
23
+ const obj = JSON.parse(fs.readFileSync(file, 'utf-8'));
24
+ if (obj && typeof obj === 'object' && 'ids' in obj && typeof obj.ids === 'object') {
25
+ return obj;
26
+ }
27
+ return emptyAllowed();
28
+ }
29
+ catch {
30
+ return emptyAllowed();
31
+ }
32
+ }
33
+ export function saveAllowed(file, state) {
34
+ fs.mkdirSync(path.dirname(file), { recursive: true });
35
+ const tmp = `${file}.${process.pid}.tmp`;
36
+ fs.writeFileSync(tmp, JSON.stringify(state), 'utf-8');
37
+ fs.renameSync(tmp, file);
38
+ }
39
+ export function isAllowed(state, id) {
40
+ return id != null && Object.prototype.hasOwnProperty.call(state.ids, String(id));
41
+ }
42
+ export function addAllowed(state, id, now) {
43
+ return { ids: { ...state.ids, [String(id)]: now } };
44
+ }
45
+ export async function isSenderAllowed(connectorId, senderId, deps) {
46
+ const policy = deps?.policy ?? (await loadGatePolicy());
47
+ if (!policy.accessPassword)
48
+ return true;
49
+ return isAllowed(loadAllowed(allowFileFor(connectorId)), senderId);
50
+ }
51
+ export function makeThrottle(maxAttempts = 5, windowMs = 60_000) {
52
+ const hits = new Map();
53
+ return function allowAttempt(id, now = Date.now()) {
54
+ const key = String(id);
55
+ const rec = hits.get(key);
56
+ if (!rec || now - rec.start >= windowMs) {
57
+ hits.set(key, { start: now, count: 1 });
58
+ return true;
59
+ }
60
+ if (rec.count >= maxAttempts)
61
+ return false;
62
+ rec.count += 1;
63
+ return true;
64
+ };
65
+ }
@@ -0,0 +1,2 @@
1
+ import type { AttachmentMaterializer } from './types.ts';
2
+ export declare const attachmentMaterializer: AttachmentMaterializer;
@@ -0,0 +1,11 @@
1
+ import { saveUploadBytes } from "../uploads.js";
2
+ export const attachmentMaterializer = {
3
+ async store(bytes, opts = {}) {
4
+ const stored = await saveUploadBytes(bytes, opts);
5
+ return {
6
+ name: stored.name,
7
+ ...(stored.mime ? { mime: stored.mime } : {}),
8
+ size: stored.size,
9
+ };
10
+ },
11
+ };
@@ -0,0 +1,4 @@
1
+ import type { GatePolicy } from './types.ts';
2
+ export declare function buildPolicy(dbSettings?: Record<string, unknown>): GatePolicy;
3
+ export declare function loadAgentId(): Promise<string | undefined>;
4
+ export declare function loadGatePolicy(): Promise<GatePolicy>;
@@ -0,0 +1,19 @@
1
+ import { SYSTEM_SETTINGS_ID } from "../system-settings.js";
2
+ export function buildPolicy(dbSettings = {}) {
3
+ const db = dbSettings;
4
+ return {
5
+ ...(typeof db.agent_id === 'string' && db.agent_id ? { agentId: db.agent_id } : {}),
6
+ accessPassword: db.access_password ?? '',
7
+ triggerPrefix: db.trigger_prefix ?? '',
8
+ replyWindow: Number(db.reply_window ?? 1800) || 1800,
9
+ };
10
+ }
11
+ export async function loadAgentId() {
12
+ const { getPluginSettings } = await import("../plugin-runtime.js");
13
+ const value = (await getPluginSettings(SYSTEM_SETTINGS_ID))['agent_id'];
14
+ return typeof value === 'string' && value ? value : undefined;
15
+ }
16
+ export async function loadGatePolicy() {
17
+ const { getPluginSettings } = await import("../plugin-runtime.js");
18
+ return buildPolicy(await getPluginSettings(SYSTEM_SETTINGS_ID));
19
+ }
@@ -0,0 +1,17 @@
1
+ export type LogTurn = {
2
+ connector: string;
3
+ chatId: string;
4
+ userId: string;
5
+ role: 'user' | 'assistant';
6
+ text: string;
7
+ tokensIn: number | null;
8
+ tokensOut: number | null;
9
+ ms: number | null;
10
+ agentId: string | null;
11
+ presetId: string | null;
12
+ };
13
+ export interface LogDb {
14
+ logTurn(row: LogTurn): void;
15
+ close(): void;
16
+ }
17
+ export declare function openLogDb(): LogDb;
@@ -0,0 +1,12 @@
1
+ import { logMessage } from "../msg-log.js";
2
+ import { getLogger } from '@coffer-org/sdk/logger';
3
+ const log = getLogger('orchestrator');
4
+ export function openLogDb() {
5
+ return {
6
+ logTurn(row) {
7
+ logMessage(row).catch((e) => log.error('logTurn failed', e));
8
+ },
9
+ close() {
10
+ },
11
+ };
12
+ }
@@ -0,0 +1,9 @@
1
+ export interface DiagnosticEvent {
2
+ ts: string;
3
+ level: 'error' | 'warn';
4
+ source: string;
5
+ message: string;
6
+ }
7
+ export declare function recordDiagnostic(level: DiagnosticEvent['level'], source: string, message: string): void;
8
+ export declare function listDiagnostics(limit?: number): DiagnosticEvent[];
9
+ export declare function clearDiagnostics(): void;
@@ -0,0 +1,13 @@
1
+ const MAX_EVENTS = 100;
2
+ const events = [];
3
+ export function recordDiagnostic(level, source, message) {
4
+ events.unshift({ ts: new Date().toISOString(), level, source, message: message.slice(0, 500) });
5
+ if (events.length > MAX_EVENTS)
6
+ events.length = MAX_EVENTS;
7
+ }
8
+ export function listDiagnostics(limit = 25) {
9
+ return events.slice(0, Math.max(1, Math.min(100, Math.trunc(limit))));
10
+ }
11
+ export function clearDiagnostics() {
12
+ events.length = 0;
13
+ }
@@ -0,0 +1,3 @@
1
+ export declare function systemTimeZone(): string;
2
+ export declare function todayDateString(now?: Date, timeZone?: string): string;
3
+ export declare function currentEnvironment(now?: Date, timeZone?: string): string;
@@ -0,0 +1,30 @@
1
+ export function systemTimeZone() {
2
+ try {
3
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
4
+ }
5
+ catch {
6
+ return 'UTC';
7
+ }
8
+ }
9
+ function part(now, timeZone, locale, options) {
10
+ return new Intl.DateTimeFormat(locale, { ...options, timeZone }).format(now);
11
+ }
12
+ export function todayDateString(now = new Date(), timeZone = systemTimeZone()) {
13
+ return part(now, timeZone, 'en-CA', { year: 'numeric', month: '2-digit', day: '2-digit' });
14
+ }
15
+ export function currentEnvironment(now = new Date(), timeZone = systemTimeZone()) {
16
+ let zone = timeZone;
17
+ try {
18
+ new Intl.DateTimeFormat('en-CA', { timeZone: zone }).format(now);
19
+ }
20
+ catch {
21
+ zone = 'UTC';
22
+ }
23
+ const date = part(now, zone, 'en-CA', { year: 'numeric', month: '2-digit', day: '2-digit' });
24
+ const weekday = part(now, zone, 'en-US', { weekday: 'long' });
25
+ const time = part(now, zone, 'en-GB', { hour: '2-digit', minute: '2-digit', hour12: false });
26
+ return [
27
+ `Current moment, refreshed on every turn: ${date} (${weekday}), ${time}, timezone ${zone}.`,
28
+ 'Resolve every relative date against this value — "today", "tomorrow", "this week", ages, and expiry, warranty or due-date checks. Never infer the date from your training data and never tell the user you cannot know it.',
29
+ ].join('\n');
30
+ }
@@ -0,0 +1,3 @@
1
+ import type { AgentToolContentResult, AttachmentRef } from './types.ts';
2
+ export declare function inspectUpload(name: string, ref?: Pick<AttachmentRef, 'mime' | 'size' | 'label'>): Promise<AgentToolContentResult>;
3
+ export declare function isAgentToolContentResult(value: unknown): value is AgentToolContentResult;
@@ -0,0 +1,61 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { basename, join } from 'node:path';
3
+ import { mimeForName } from "../file-fields.js";
4
+ import { uploadsDir } from "../uploads.js";
5
+ const MAX_VISION_BYTES = 5 * 1024 * 1024;
6
+ const MAX_DOCUMENT_BYTES = 10 * 1024 * 1024;
7
+ const MAX_TEXT_BYTES = 512 * 1024;
8
+ const textResult = (text, isError = false) => ({
9
+ __cofferToolContent: true,
10
+ content: [{ type: 'text', text }],
11
+ ...(isError ? { isError: true } : {}),
12
+ });
13
+ export async function inspectUpload(name, ref = {}) {
14
+ if (!name || basename(name) !== name)
15
+ return textResult('Invalid upload name.', true);
16
+ const file = join(uploadsDir(), name);
17
+ let size;
18
+ try {
19
+ size = (await stat(file)).size;
20
+ }
21
+ catch {
22
+ return textResult(`File is no longer present on the server: ${name}`, true);
23
+ }
24
+ const mime = ref.mime ?? mimeForName(name);
25
+ if (mime === 'image/jpeg' || mime === 'image/png' || mime === 'image/gif' || mime === 'image/webp') {
26
+ if (size > MAX_VISION_BYTES)
27
+ return textResult(`Image is ${size} bytes, above the ${MAX_VISION_BYTES}-byte inspection limit.`, true);
28
+ const bytes = await readFile(file);
29
+ return {
30
+ __cofferToolContent: true,
31
+ content: [
32
+ { type: 'text', text: `Image: ${ref.label ?? name} (${mime}, ${size} bytes).` },
33
+ { type: 'image', mime, data: bytes.toString('base64') },
34
+ ],
35
+ };
36
+ }
37
+ if (mime === 'application/pdf') {
38
+ if (size > MAX_DOCUMENT_BYTES)
39
+ return textResult(`PDF is ${size} bytes, above the ${MAX_DOCUMENT_BYTES}-byte inspection limit.`, true);
40
+ const bytes = await readFile(file);
41
+ return {
42
+ __cofferToolContent: true,
43
+ content: [
44
+ { type: 'text', text: `PDF: ${ref.label ?? name} (${size} bytes).` },
45
+ { type: 'document', mime: 'application/pdf', data: bytes.toString('base64') },
46
+ ],
47
+ };
48
+ }
49
+ if (mime?.startsWith('text/') || mime === 'application/json' || mime === 'text/csv') {
50
+ if (size > MAX_TEXT_BYTES)
51
+ return textResult(`Text file is too large to inspect: ${size} bytes.`, true);
52
+ return textResult(`Text file: ${ref.label ?? name}\n\n${(await readFile(file)).toString('utf8')}`);
53
+ }
54
+ return textResult(`This file type cannot be inspected by the agent (${mime ?? 'unknown'}). ` +
55
+ 'It can still be attached to a record.', true);
56
+ }
57
+ export function isAgentToolContentResult(value) {
58
+ return (typeof value === 'object' &&
59
+ value !== null &&
60
+ value.__cofferToolContent === true);
61
+ }
@@ -0,0 +1 @@
1
+ export declare function chunk(text: string, max: number): string[];
@@ -0,0 +1,6 @@
1
+ export function chunk(text, max) {
2
+ const out = [];
3
+ for (let i = 0; i < text.length; i += max)
4
+ out.push(text.slice(i, i + max));
5
+ return out.length ? out : [''];
6
+ }
@@ -0,0 +1,30 @@
1
+ import type { BackgroundTask } from '../background-scheduler.ts';
2
+ import { listRecentMessageMetrics } from '../msg-log.ts';
3
+ import { listDiagnostics } from './diagnostics.ts';
4
+ export { handleIncoming } from './pipeline.ts';
5
+ export { registerAgent, resolveAgent, registerConnector, isConnectorRegistered, clearRuntimeRegistries, listRegisteredAgents, listRegisteredConnectors, listAgentCatalog, getDefaultAgentId, liveAgentId, } from './registry.ts';
6
+ export { attachmentMaterializer } from './attachments.ts';
7
+ export { makeAttachmentCapabilities } from './agent-capabilities.ts';
8
+ export { makeSystemCapabilities } from './system-capabilities.ts';
9
+ export { makeSuggestionCapabilities } from './suggestion-capabilities.ts';
10
+ export type { SuggestionCapability } from './suggestion-capabilities.ts';
11
+ export { currentEnvironment, systemTimeZone } from './environment.ts';
12
+ export { inspectUpload, isAgentToolContentResult } from './file-inspection.ts';
13
+ export type { PipelineDeps, RunAgentFn } from './pipeline.ts';
14
+ export { buildPolicy, loadGatePolicy, loadAgentId } from './config.ts';
15
+ export { getConversationStarters, refreshSystemStarters } from './starters.ts';
16
+ export { isSenderAllowed } from './allow.ts';
17
+ export { makeLiveChannel, plainRender } from './live-message.ts';
18
+ export type { LiveChannelOps, LiveChannelOpts, RenderFn } from './live-message.ts';
19
+ export { chunk } from './format.ts';
20
+ export type { Connector, IncomingConversation, GatePolicy, ReplyContext, ReplyPayload, ReplyChannel, AttachmentRef, AttachmentMaterializer, AgentToolDefinition, AgentToolProvider, ConvMessage, SystemLayer, AgentRequest, AgentResult, AgentRuntime, AgentCapabilities, AgentPreset, AgentDescriptor, ConnectorRegistration, AgentToolContentBlock, AgentToolContentResult, } from './types.ts';
21
+ export declare function startOrchestrator(): void;
22
+ export declare function stopOrchestrator(): void;
23
+ export declare const orchestratorStartersTask: BackgroundTask;
24
+ export declare function orchestratorDiagnostics(): Promise<{
25
+ generatedAt: string;
26
+ agents: string[];
27
+ connectors: string[];
28
+ recentMessages: Awaited<ReturnType<typeof listRecentMessageMetrics>>;
29
+ recentErrors: ReturnType<typeof listDiagnostics>;
30
+ }>;
@@ -0,0 +1,56 @@
1
+ import { openLogDb } from "./db.js";
2
+ import { listRecentMessageMetrics } from "../msg-log.js";
3
+ import { listRegisteredAgents, listRegisteredConnectors } from "./registry.js";
4
+ import { listDiagnostics } from "./diagnostics.js";
5
+ import { setLogDb } from "./pipeline.js";
6
+ import { refreshSystemStarters } from "./starters.js";
7
+ import { getLogger } from '@coffer-org/sdk/logger';
8
+ const log = getLogger('orchestrator');
9
+ export { handleIncoming } from "./pipeline.js";
10
+ export { registerAgent, resolveAgent, registerConnector, isConnectorRegistered, clearRuntimeRegistries, listRegisteredAgents, listRegisteredConnectors, listAgentCatalog, getDefaultAgentId, liveAgentId, } from "./registry.js";
11
+ export { attachmentMaterializer } from "./attachments.js";
12
+ export { makeAttachmentCapabilities } from "./agent-capabilities.js";
13
+ export { makeSystemCapabilities } from "./system-capabilities.js";
14
+ export { makeSuggestionCapabilities } from "./suggestion-capabilities.js";
15
+ export { currentEnvironment, systemTimeZone } from "./environment.js";
16
+ export { inspectUpload, isAgentToolContentResult } from "./file-inspection.js";
17
+ export { buildPolicy, loadGatePolicy, loadAgentId } from "./config.js";
18
+ export { getConversationStarters, refreshSystemStarters } from "./starters.js";
19
+ export { isSenderAllowed } from "./allow.js";
20
+ import { carryLegacyState } from "./allow.js";
21
+ export { makeLiveChannel, plainRender } from "./live-message.js";
22
+ export { chunk } from "./format.js";
23
+ let db;
24
+ const STARTERS_CHECK_INTERVAL_MS = 10 * 60_000;
25
+ function runStartersRefresh() {
26
+ return refreshSystemStarters().catch((err) => log.warn(`system starters: ${String(err)}`));
27
+ }
28
+ export function startOrchestrator() {
29
+ carryLegacyState();
30
+ db = openLogDb();
31
+ setLogDb(db);
32
+ void runStartersRefresh();
33
+ }
34
+ export function stopOrchestrator() {
35
+ try {
36
+ db?.close();
37
+ }
38
+ catch {
39
+ }
40
+ db = undefined;
41
+ setLogDb(undefined);
42
+ }
43
+ export const orchestratorStartersTask = {
44
+ name: 'core:orchestrator-starters',
45
+ intervalMs: STARTERS_CHECK_INTERVAL_MS,
46
+ run: runStartersRefresh,
47
+ };
48
+ export async function orchestratorDiagnostics() {
49
+ return {
50
+ generatedAt: new Date().toISOString(),
51
+ agents: listRegisteredAgents(),
52
+ connectors: listRegisteredConnectors(),
53
+ recentMessages: await listRecentMessageMetrics(25),
54
+ recentErrors: listDiagnostics(25),
55
+ };
56
+ }
@@ -0,0 +1,14 @@
1
+ import type { ReplyChannel, ReplyPayload } from './types.ts';
2
+ export interface LiveChannelOps {
3
+ send(text: string): Promise<string | null>;
4
+ edit(msgId: string, text: string): Promise<void>;
5
+ }
6
+ export type RenderFn = (r: ReplyPayload) => string[];
7
+ export interface LiveChannelOpts {
8
+ ops: LiveChannelOps;
9
+ throttleMs: number;
10
+ render: RenderFn;
11
+ maxLength: number;
12
+ }
13
+ export declare function plainRender(max: number): RenderFn;
14
+ export declare function makeLiveChannel(o: LiveChannelOpts): ReplyChannel;
@@ -0,0 +1,100 @@
1
+ import { chunk } from "./format.js";
2
+ import { getLogger } from '@coffer-org/sdk/logger';
3
+ const log = getLogger('orchestrator');
4
+ export function plainRender(max) {
5
+ return (r) => chunk((r.text ?? '').trim() || '⚠️ the agent returned no response', max);
6
+ }
7
+ async function swallow(fn, fallback, label) {
8
+ try {
9
+ return await fn();
10
+ }
11
+ catch (err) {
12
+ log.error(`live-message ${label}: ${err instanceof Error ? err.message : String(err)}`);
13
+ return fallback;
14
+ }
15
+ }
16
+ export function makeLiveChannel(o) {
17
+ let msgId = null;
18
+ let pending = '';
19
+ let lastSent = '';
20
+ let busy = false;
21
+ let closed = false;
22
+ let gen = 0;
23
+ let inFlight = Promise.resolve();
24
+ const timer = setInterval(() => {
25
+ if (closed || busy)
26
+ return;
27
+ const t = pending.slice(0, o.maxLength);
28
+ if (!t || t === lastSent)
29
+ return;
30
+ busy = true;
31
+ const myGen = gen;
32
+ if (!msgId) {
33
+ inFlight = o.ops
34
+ .send(t)
35
+ .then((id) => {
36
+ if (gen === myGen) {
37
+ msgId = id;
38
+ lastSent = t;
39
+ }
40
+ })
41
+ .catch((err) => {
42
+ log.error(`live-message send: ${err instanceof Error ? err.message : String(err)}`);
43
+ })
44
+ .finally(() => {
45
+ busy = false;
46
+ });
47
+ return;
48
+ }
49
+ const id = msgId;
50
+ inFlight = o.ops
51
+ .edit(id, t)
52
+ .then(() => {
53
+ if (gen === myGen)
54
+ lastSent = t;
55
+ })
56
+ .catch((err) => {
57
+ log.error(`live-message edit: ${err instanceof Error ? err.message : String(err)}`);
58
+ })
59
+ .finally(() => {
60
+ busy = false;
61
+ });
62
+ }, o.throttleMs);
63
+ if (typeof timer.unref === 'function')
64
+ timer.unref();
65
+ return {
66
+ update(text) {
67
+ pending = text;
68
+ },
69
+ segment() {
70
+ gen++;
71
+ msgId = null;
72
+ pending = '';
73
+ lastSent = '';
74
+ },
75
+ async finish(r) {
76
+ closed = true;
77
+ clearInterval(timer);
78
+ await inFlight;
79
+ const parts = o.render(r);
80
+ let last = null;
81
+ const [head, ...rest] = parts;
82
+ if (msgId && head !== undefined) {
83
+ const id = msgId;
84
+ if (head !== lastSent) {
85
+ await swallow(() => o.ops.edit(id, head), undefined, 'edit(final)');
86
+ lastSent = head;
87
+ }
88
+ last = id;
89
+ }
90
+ else if (head !== undefined) {
91
+ last = await swallow(() => o.ops.send(head), null, 'send(final)');
92
+ }
93
+ for (const p of rest) {
94
+ const id = await swallow(() => o.ops.send(p), null, 'send(overflow)');
95
+ last = id ?? last;
96
+ }
97
+ return last;
98
+ },
99
+ };
100
+ }
@@ -0,0 +1,11 @@
1
+ import type { Connector, IncomingConversation, GatePolicy, AgentRuntime } from './types.ts';
2
+ import type { LogDb } from './db.ts';
3
+ export declare function setLogDb(db: LogDb | undefined): void;
4
+ export type RunAgentFn = AgentRuntime['run'];
5
+ export interface PipelineDeps {
6
+ runAgent?: RunAgentFn;
7
+ logDb?: LogDb | null;
8
+ policy?: GatePolicy;
9
+ agentBase?: () => Promise<string>;
10
+ }
11
+ export declare function handleIncoming(connector: Connector, conversation: IncomingConversation, deps?: PipelineDeps): Promise<void>;