@coffer-org/server 7.1.0 → 7.2.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.
@@ -17,6 +17,7 @@ export interface BackgroundScheduler {
17
17
  start(): void;
18
18
  stop(): void;
19
19
  }
20
+ export declare const DEFAULT_TASK_TIMEOUT_MS = 600000;
20
21
  export declare function makeScheduler(opts?: SchedulerOpts): BackgroundScheduler;
21
22
  export declare function startScheduler(tasks: BackgroundTask[], opts?: SchedulerOpts): void;
22
23
  export declare function stopScheduler(): void;
@@ -6,10 +6,11 @@ const defaultSetTimer = (cb, ms) => {
6
6
  t.unref();
7
7
  return t;
8
8
  };
9
+ export const DEFAULT_TASK_TIMEOUT_MS = 600_000;
9
10
  export function makeScheduler(opts = {}) {
10
11
  const startupDelayMs = opts.startupDelayMs ?? (Number(process.env['BG_STARTUP_DELAY_MS']) || 600_000);
11
12
  const gapMs = opts.gapMs ?? (Number(process.env['BG_GAP_MS']) || 30_000);
12
- const defaultTimeoutMs = opts.taskTimeoutMs ?? (Number(process.env['BG_TASK_TIMEOUT_MS']) || 600_000);
13
+ const defaultTimeoutMs = opts.taskTimeoutMs ?? (Number(process.env['BG_TASK_TIMEOUT_MS']) || DEFAULT_TASK_TIMEOUT_MS);
13
14
  const setTimer = opts.setTimer ?? defaultSetTimer;
14
15
  const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
15
16
  const tasks = [];
@@ -1,4 +1,30 @@
1
1
  import type { LocaleResolver } from '../locale-registry.ts';
2
+ import { type AgentMeta } from '@coffer-org/sdk/library';
3
+ import type { Condition } from '@coffer-org/sdk/condition';
4
+ interface ShelfClient {
5
+ shelf: string;
6
+ library: string;
7
+ label: string;
8
+ agent?: string;
9
+ standalone?: boolean;
10
+ fields: unknown[];
11
+ }
12
+ interface ExtendClient {
13
+ id: string;
14
+ attachTo: {
15
+ library: string;
16
+ shelf?: string;
17
+ }[];
18
+ showWhen?: Condition;
19
+ agent?: AgentMeta | string;
20
+ }
21
+ interface LibraryClient {
22
+ id: string;
23
+ label: string;
24
+ agent?: AgentMeta | string;
25
+ extends?: ExtendClient[];
26
+ shelves: ShelfClient[];
27
+ }
2
28
  export interface ShelfIndexEntry {
3
29
  library: string;
4
30
  libraryLabel: string;
@@ -25,13 +51,20 @@ export declare const FILE_WRITE_HINT: string;
25
51
  export declare const SOURCE_WRITE_HINT: string;
26
52
  export declare const DERIVED_WRITE_HINT: string;
27
53
  export declare const serverOwnedPartsHint: (roles: string[]) => string;
54
+ export interface OperatingRule {
55
+ source: string;
56
+ when?: string;
57
+ text: string;
58
+ }
28
59
  export interface ShelfDescription {
29
60
  library: string;
30
61
  shelf: string;
31
62
  label: string;
32
63
  agent?: string;
64
+ operatingRules?: OperatingRule[];
33
65
  fields: FieldInfo[];
34
66
  }
67
+ export declare function collectOperatingRules(lib: LibraryClient, shelf: string): OperatingRule[];
35
68
  interface SchemaClient {
36
69
  getSchema(): Promise<unknown>;
37
70
  }
@@ -46,6 +79,6 @@ export declare class SchemaCache {
46
79
  private load;
47
80
  index(): Promise<ShelfIndexEntry[]>;
48
81
  describeShelf(library: string, shelf: string): Promise<ShelfDescription>;
49
- private find;
82
+ private locate;
50
83
  }
51
84
  export {};
@@ -1,3 +1,6 @@
1
+ import { normalizeAgentMeta } from '@coffer-org/sdk/library';
2
+ import { extendMatches } from '@coffer-org/sdk/extend';
3
+ import { describeCondition } from '@coffer-org/sdk/condition';
1
4
  export const FILE_WRITE_HINT = 'Uploaded file only: {"name":"<filename returned by POST /api/upload>"} ' +
2
5
  '(or an array of those when the field is multiple). ' +
3
6
  'A remote URL is rejected — the server stores files, not links. ' +
@@ -13,6 +16,22 @@ export const DERIVED_WRITE_HINT = 'Read-only — the server recomputes this fiel
13
16
  export const serverOwnedPartsHint = (roles) => `Partly read-only — the server owns ${roles.map((r) => `\`${r}\``).join(', ')} in this value and ` +
14
17
  `computes ${roles.length > 1 ? 'them' : 'it'} on every write. Send the other roles only; anything ` +
15
18
  `sent for these is discarded, and a read returns the server's value.`;
19
+ export function collectOperatingRules(lib, shelf) {
20
+ const out = [];
21
+ const libAgent = normalizeAgentMeta(lib.agent);
22
+ if (libAgent?.instructions)
23
+ out.push({ source: lib.id, text: libAgent.instructions });
24
+ for (const e of lib.extends ?? []) {
25
+ if (!extendMatches(e, lib.id, shelf))
26
+ continue;
27
+ const meta = normalizeAgentMeta(e.agent);
28
+ if (!meta?.instructions)
29
+ continue;
30
+ const when = describeCondition(e.showWhen, (f) => f).join(' and ');
31
+ out.push({ source: e.id, ...(when ? { when } : {}), text: meta.instructions });
32
+ }
33
+ return out;
34
+ }
16
35
  function serverOwnedRoles(t) {
17
36
  const parts = t.parts ?? [];
18
37
  return parts.filter((p) => p.mode === 'computedStored').map((p) => p.role);
@@ -89,20 +108,24 @@ export class SchemaCache {
89
108
  })));
90
109
  }
91
110
  async describeShelf(library, shelf) {
92
- let m = this.find(await this.load(), library, shelf);
93
- if (!m)
94
- m = this.find(await this.load(true), library, shelf);
95
- if (!m)
111
+ let at = this.locate(await this.load(), library, shelf);
112
+ if (!at)
113
+ at = this.locate(await this.load(true), library, shelf);
114
+ if (!at)
96
115
  throw new Error(`unknown_shelf ${library}/${shelf}`);
116
+ const rules = collectOperatingRules(at.lib, shelf);
97
117
  return {
98
118
  library,
99
119
  shelf,
100
- label: this.display(m.label, m.shelf),
101
- ...(m.agent ? { agent: m.agent } : {}),
102
- fields: flattenFields(m.fields),
120
+ label: this.display(at.m.label, at.m.shelf),
121
+ ...(at.m.agent ? { agent: at.m.agent } : {}),
122
+ ...(rules.length ? { operatingRules: rules } : {}),
123
+ fields: flattenFields(at.m.fields),
103
124
  };
104
125
  }
105
- find(libraries, library, shelf) {
106
- return libraries.find((v) => v.id === library)?.shelves.find((m) => m.shelf === shelf);
126
+ locate(libraries, library, shelf) {
127
+ const lib = libraries.find((v) => v.id === library);
128
+ const m = lib?.shelves.find((s) => s.shelf === shelf);
129
+ return lib && m ? { lib, m } : undefined;
107
130
  }
108
131
  }
@@ -53,7 +53,9 @@ export function buildTools(client, cache, locales) {
53
53
  },
54
54
  {
55
55
  name: 'describe_shelf',
56
- description: 'Detailed shelf schema: the shelf note (agent), then fields with kind/prim/required, relation/options, and ' +
56
+ description: 'Detailed shelf schema: the shelf note (agent), the operating rules that govern writing here ' +
57
+ '(operatingRules — from the library and from any extra field-set attached to this shelf, each ' +
58
+ 'with the condition it applies under), then fields with kind/prim/required, relation/options, and ' +
57
59
  'a per-field note (agent) saying what that field means. A collection is one entry with multiple:true and its ' +
58
60
  'row shape in fields[]. A field with derived:true is computed by the server and rejects writes — omit it when ' +
59
61
  'writing. Call BEFORE create_record/update_record.',
@@ -4,6 +4,7 @@ import { type ToolResult } from './mcp-contract/tools.ts';
4
4
  import { type LocaleResolver } from './locale-registry.ts';
5
5
  import { type AuthRole, type PluginHooks } from './plugin-hooks.ts';
6
6
  import { type Condition } from '@coffer-org/sdk/condition';
7
+ import { type AgentMeta } from '@coffer-org/sdk/library';
7
8
  import { type RagHit } from './rag-search.ts';
8
9
  export interface McpToolDef {
9
10
  server: string;
@@ -58,12 +59,12 @@ export declare function collectLibraryPurposes(reg?: {
58
59
  meta: {
59
60
  id: string;
60
61
  label?: string;
61
- agent?: string;
62
+ agent?: AgentMeta | string;
62
63
  };
63
64
  }[];
64
65
  extends_: {
65
66
  id: string;
66
- agent?: string;
67
+ agent?: AgentMeta | string;
67
68
  showWhen?: Condition;
68
69
  attachTo: {
69
70
  library: string;
package/dist/mcp-tools.js CHANGED
@@ -10,6 +10,7 @@ import { pluginHooks, pluginCtx } from "./plugin-hooks.js";
10
10
  import { getActiveRegistry } from "./registry-context.js";
11
11
  import { countTargetsFor, recordCounts } from "./counts.js";
12
12
  import { describeCondition } from '@coffer-org/sdk/condition';
13
+ import { normalizeAgentMeta } from '@coffer-org/sdk/library';
13
14
  import { getEm } from "./db.js";
14
15
  import { getPluginSettings } from "./plugin-runtime.js";
15
16
  import { configuredPublicUrl } from "./public-url.js";
@@ -296,16 +297,19 @@ export function collectLibraryPurposes(reg, locales) {
296
297
  if (!registry)
297
298
  return [];
298
299
  return registry.libraries
299
- .filter((v) => v.meta.agent)
300
+ .filter((v) => normalizeAgentMeta(v.meta.agent)?.description)
300
301
  .map((v) => ({
301
302
  id: v.meta.id,
302
303
  name: (v.meta.label ? locales?.resolve(v.meta.label) : undefined) ?? v.meta.id,
303
- agent: v.meta.agent,
304
- extends: registry.extends_.flatMap((e) => e.agent
305
- ? e.attachTo
306
- .filter((a) => a.library === v.meta.id)
307
- .map((a) => ({ id: e.id, shelf: a.shelf, agent: e.agent, showWhen: e.showWhen }))
308
- : []),
304
+ agent: normalizeAgentMeta(v.meta.agent).description,
305
+ extends: registry.extends_.flatMap((e) => {
306
+ const meta = normalizeAgentMeta(e.agent);
307
+ return meta?.description
308
+ ? e.attachTo
309
+ .filter((a) => a.library === v.meta.id)
310
+ .map((a) => ({ id: e.id, shelf: a.shelf, agent: meta.description, showWhen: e.showWhen }))
311
+ : [];
312
+ }),
309
313
  }));
310
314
  }
311
315
  export async function siteSection(siteUrl) {
@@ -17,6 +17,7 @@ export { isSenderAllowed } from './allow.ts';
17
17
  export { makeLiveChannel, plainRender } from './live-message.ts';
18
18
  export type { LiveChannelOps, LiveChannelOpts, RenderFn } from './live-message.ts';
19
19
  export { chunk } from './format.ts';
20
+ export { DEFAULT_TASK_TIMEOUT_MS } from '../background-scheduler.ts';
20
21
  export type { Connector, IncomingConversation, GatePolicy, ReplyContext, ReplyPayload, ReplyChannel, AttachmentRef, AttachmentMaterializer, AgentToolDefinition, AgentToolProvider, ConvMessage, AgentRequest, AgentResult, AgentRuntime, AgentCapabilities, AgentPreset, AgentDescriptor, ConnectorRegistration, AgentToolContentBlock, AgentToolContentResult, } from './types.ts';
21
22
  export declare function startOrchestrator(): void;
22
23
  export declare function stopOrchestrator(): void;
@@ -20,6 +20,7 @@ export { isSenderAllowed } from "./allow.js";
20
20
  import { carryLegacyState } from "./allow.js";
21
21
  export { makeLiveChannel, plainRender } from "./live-message.js";
22
22
  export { chunk } from "./format.js";
23
+ export { DEFAULT_TASK_TIMEOUT_MS } from "../background-scheduler.js";
23
24
  let db;
24
25
  const STARTERS_CHECK_INTERVAL_MS = 10 * 60_000;
25
26
  function runStartersRefresh() {
@@ -60,11 +60,16 @@ export interface AgentDescriptor {
60
60
  title: string;
61
61
  presets: AgentPreset[];
62
62
  }
63
+ export interface AgentBudget {
64
+ responseTimeout?: number;
65
+ toolRounds?: number;
66
+ }
63
67
  export interface AgentRequest {
64
68
  system: string;
65
69
  messages: ConvMessage[];
66
70
  toolProvider?: AgentToolProvider;
67
71
  presetId?: string;
72
+ budget?: AgentBudget;
68
73
  onDelta?: (accumulated: string) => void;
69
74
  onReasoning?: (accumulated: string) => void;
70
75
  onSegment?: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffer-org/server",
3
- "version": "7.1.0",
3
+ "version": "7.2.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -36,7 +36,7 @@
36
36
  "postpack": "node ../../scripts/swap-exports.mjs src"
37
37
  },
38
38
  "dependencies": {
39
- "@coffer-org/sdk": "^7.1.0",
39
+ "@coffer-org/sdk": "^7.2.0",
40
40
  "@extractus/oembed-extractor": "^4.1.0",
41
41
  "@fastify/cors": "^11.2.0",
42
42
  "@fastify/multipart": "^10.0.0",