negotium 0.1.38 → 0.1.39
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 +19 -0
- package/dist/agent-helpers.js +16227 -6264
- package/dist/agent-helpers.js.map +82 -35
- package/dist/browser-runtime.js +1600 -0
- package/dist/browser-runtime.js.map +28 -0
- package/dist/hosted-agent.js +2 -2
- package/dist/hosted-agent.js.map +2 -2
- package/dist/main.js +5041 -4533
- package/dist/main.js.map +22 -22
- package/dist/mcp-factories.js +15141 -301
- package/dist/mcp-factories.js.map +100 -7
- package/dist/prompts.js +117 -49
- package/dist/prompts.js.map +3 -3
- package/dist/query-runtime.js +309 -1
- package/dist/query-runtime.js.map +9 -4
- package/dist/runtime/src/agents/archiver.ts +407 -296
- package/dist/runtime/src/agents/mcp-tools/ask-user.ts +440 -274
- package/dist/runtime/src/agents/mcp-tools/self-config.ts +139 -65
- package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +1037 -816
- package/dist/runtime/src/agents/public-helpers.ts +82 -0
- package/dist/runtime/src/agents/self-config-core.ts +574 -278
- package/dist/runtime/src/agents/topic-cleanup.ts +167 -129
- package/dist/runtime/src/index.ts +4 -1
- package/dist/runtime/src/mcp/factories/index.ts +9 -0
- package/dist/runtime/src/mcp/session-comm/topic-catalog.ts +132 -0
- package/dist/runtime/src/mcp/session-comm/topics.ts +23 -57
- package/dist/runtime/src/platform/playwright/manager.ts +323 -50
- package/dist/runtime/src/platform/playwright/public-runtime.ts +45 -0
- package/dist/runtime/src/prompts/builders.ts +192 -51
- package/dist/runtime/src/query/public-runtime.ts +8 -0
- package/dist/runtime/src/query/state.ts +69 -33
- package/dist/runtime/src/version.ts +1 -1
- package/dist/types/packages/core/src/agents/api-topic-agent-switch.d.ts +38 -0
- package/dist/types/packages/core/src/agents/archiver.d.ts +52 -16
- package/dist/types/packages/core/src/agents/mcp-tools/ask-user.d.ts +75 -4
- package/dist/types/packages/core/src/agents/mcp-tools/self-config.d.ts +17 -0
- package/dist/types/packages/core/src/agents/mcp-tools/spawn-subagent.d.ts +112 -25
- package/dist/types/packages/core/src/agents/public-helpers.d.ts +7 -0
- package/dist/types/packages/core/src/agents/self-config-core.d.ts +185 -0
- package/dist/types/packages/core/src/agents/topic-cleanup.d.ts +30 -24
- package/dist/types/packages/core/src/mcp/factories/index.d.ts +1 -0
- package/dist/types/packages/core/src/mcp/session-comm/topic-catalog.d.ts +46 -0
- package/dist/types/packages/core/src/platform/playwright/manager.d.ts +56 -2
- package/dist/types/packages/core/src/platform/playwright/public-runtime.d.ts +4 -0
- package/dist/types/packages/core/src/prompts/builders.d.ts +29 -3
- package/dist/types/packages/core/src/query/public-runtime.d.ts +1 -0
- package/dist/types/packages/core/src/query/state.d.ts +13 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +6 -1
|
@@ -175,6 +175,44 @@ export interface SessionSystemPromptOpts {
|
|
|
175
175
|
fileDeliveryTools?: boolean;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
export type SessionPromptKind = "topic" | "channel" | "manager";
|
|
179
|
+
export type PromptSectionSlot =
|
|
180
|
+
| "after-runtime-tools"
|
|
181
|
+
| "after-shared-tasks"
|
|
182
|
+
| "before-session-communication"
|
|
183
|
+
| "after-session-communication"
|
|
184
|
+
| "before-topic-configuration"
|
|
185
|
+
| "after-topic-configuration"
|
|
186
|
+
| "after-system-prompt";
|
|
187
|
+
|
|
188
|
+
export interface PromptSectionContext extends SessionSystemPromptOpts {
|
|
189
|
+
sessionKind: SessionPromptKind;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface PromptExtraSection {
|
|
193
|
+
id: string;
|
|
194
|
+
slot: PromptSectionSlot;
|
|
195
|
+
order?: number;
|
|
196
|
+
render(context: PromptSectionContext): string | null | undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export interface PromptTemplateRequest {
|
|
200
|
+
kind: "topic-system" | "channel-system" | "manager-system" | "visual-design";
|
|
201
|
+
filename: string;
|
|
202
|
+
fallback: string;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface PromptBuilderHost {
|
|
206
|
+
readonly loadTemplate?: (request: PromptTemplateRequest) => string | null | undefined;
|
|
207
|
+
readonly extraSections?: readonly PromptExtraSection[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface PromptBuilders {
|
|
211
|
+
buildTopicSystemPrompt(opts: SessionSystemPromptOpts): string;
|
|
212
|
+
buildChannelSystemPrompt(opts: SessionSystemPromptOpts): string;
|
|
213
|
+
buildManagerSystemPrompt(opts: SessionSystemPromptOpts): string;
|
|
214
|
+
}
|
|
215
|
+
|
|
178
216
|
interface RuntimeToolSectionOpts {
|
|
179
217
|
agentKind: AgentKind;
|
|
180
218
|
canSpawnSubagents?: boolean;
|
|
@@ -187,7 +225,15 @@ interface RuntimeToolSectionOpts {
|
|
|
187
225
|
subagentReportMode?: SubagentReportMode;
|
|
188
226
|
}
|
|
189
227
|
|
|
190
|
-
|
|
228
|
+
interface RuntimeToolSectionExtensions {
|
|
229
|
+
render(slot: PromptSectionSlot): string[];
|
|
230
|
+
visualDesignGuide: string;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function buildRuntimeToolSection(
|
|
234
|
+
opts: RuntimeToolSectionOpts,
|
|
235
|
+
extensions: RuntimeToolSectionExtensions,
|
|
236
|
+
): string {
|
|
191
237
|
const {
|
|
192
238
|
agentKind,
|
|
193
239
|
canSpawnSubagents = false,
|
|
@@ -282,13 +328,16 @@ function buildRuntimeToolSection(opts: RuntimeToolSectionOpts): string {
|
|
|
282
328
|
'Do not use provider built-in "AskUserQuestion"; it is disabled or unsupported in this headless chat runtime. Use the runtime ask_user_question tool instead.',
|
|
283
329
|
]),
|
|
284
330
|
scheduleSelfToolLine,
|
|
285
|
-
...(visualTools ? ["", visualDesignGuide
|
|
331
|
+
...(visualTools && extensions.visualDesignGuide ? ["", extensions.visualDesignGuide] : []),
|
|
332
|
+
...extensions.render("after-runtime-tools"),
|
|
286
333
|
"",
|
|
287
334
|
"## Shared Tasks",
|
|
288
335
|
taskToolLine,
|
|
289
336
|
"Use this shared task store for plans, task progress, and checklist updates. It is visible across claude/codex/maestro turns and drives the live task panel.",
|
|
290
337
|
nativeTaskPolicyLine,
|
|
338
|
+
...extensions.render("after-shared-tasks"),
|
|
291
339
|
...fileDeliverySection,
|
|
340
|
+
...extensions.render("before-session-communication"),
|
|
292
341
|
"",
|
|
293
342
|
"## Session Communication",
|
|
294
343
|
...(subagentParentTitle
|
|
@@ -313,6 +362,7 @@ function buildRuntimeToolSection(opts: RuntimeToolSectionOpts): string {
|
|
|
313
362
|
: []),
|
|
314
363
|
"Do not use session communication to make another topic perform destructive changes without the user's clear intent.",
|
|
315
364
|
...spawnSubagentSection,
|
|
365
|
+
...extensions.render("after-session-communication"),
|
|
316
366
|
];
|
|
317
367
|
|
|
318
368
|
const modelCatalog = SELECTABLE_MODELS.map(
|
|
@@ -343,69 +393,160 @@ function buildRuntimeToolSection(opts: RuntimeToolSectionOpts): string {
|
|
|
343
393
|
if (agentKind !== "claude") {
|
|
344
394
|
return [
|
|
345
395
|
...shared,
|
|
396
|
+
...extensions.render("before-topic-configuration"),
|
|
346
397
|
...topicConfig,
|
|
398
|
+
...extensions.render("after-topic-configuration"),
|
|
347
399
|
"",
|
|
348
400
|
"## Runtime Tool Limits",
|
|
349
401
|
"If file delivery or topic configuration tools are not present in your available tools for this session, do not claim you used them. Tell the user this session does not expose that in-chat tool action.",
|
|
350
402
|
].join("\n");
|
|
351
403
|
}
|
|
352
404
|
|
|
353
|
-
return [
|
|
405
|
+
return [
|
|
406
|
+
...shared,
|
|
407
|
+
...extensions.render("before-topic-configuration"),
|
|
408
|
+
...topicConfig,
|
|
409
|
+
...extensions.render("after-topic-configuration"),
|
|
410
|
+
].join("\n");
|
|
354
411
|
}
|
|
355
412
|
|
|
356
|
-
export function
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
});
|
|
376
|
-
if (opts.description?.trim()) {
|
|
377
|
-
prompt += `\n\n## Topic-Specific Instructions\n${opts.description.trim()}`;
|
|
413
|
+
export function createPromptBuilders(host: PromptBuilderHost = {}): PromptBuilders {
|
|
414
|
+
const loadTemplate = host.loadTemplate;
|
|
415
|
+
const sections = (host.extraSections ?? []).map((section): PromptExtraSection => {
|
|
416
|
+
const render = section.render;
|
|
417
|
+
const snapshot: PromptExtraSection = {
|
|
418
|
+
id: section.id,
|
|
419
|
+
slot: section.slot,
|
|
420
|
+
...(section.order === undefined ? {} : { order: section.order }),
|
|
421
|
+
render(context) {
|
|
422
|
+
return render.call(snapshot, context);
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
return Object.freeze(snapshot);
|
|
426
|
+
});
|
|
427
|
+
const ids = new Set<string>();
|
|
428
|
+
for (const section of sections) {
|
|
429
|
+
if (!section.id.trim()) throw new Error("prompt extra section id is required");
|
|
430
|
+
if (ids.has(section.id)) throw new Error(`duplicate prompt extra section id: ${section.id}`);
|
|
431
|
+
ids.add(section.id);
|
|
378
432
|
}
|
|
379
|
-
|
|
380
|
-
|
|
433
|
+
sections.sort((a, b) => (a.order ?? 0) - (b.order ?? 0) || a.id.localeCompare(b.id));
|
|
434
|
+
|
|
435
|
+
const templateCache = new Map<PromptTemplateRequest["kind"], string>();
|
|
436
|
+
const template = (request: PromptTemplateRequest): string => {
|
|
437
|
+
const cached = templateCache.get(request.kind);
|
|
438
|
+
if (cached !== undefined) return cached;
|
|
439
|
+
const loaded = loadTemplate?.(request);
|
|
440
|
+
const value =
|
|
441
|
+
loaded ??
|
|
442
|
+
(request.kind === "topic-system"
|
|
443
|
+
? topicSystemPromptTemplate()
|
|
444
|
+
: request.kind === "channel-system"
|
|
445
|
+
? channelSystemPromptTemplate()
|
|
446
|
+
: request.kind === "manager-system"
|
|
447
|
+
? managerSystemPromptTemplate()
|
|
448
|
+
: visualDesignGuide());
|
|
449
|
+
templateCache.set(request.kind, value);
|
|
450
|
+
return value;
|
|
451
|
+
};
|
|
381
452
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
453
|
+
const build = (
|
|
454
|
+
sessionKind: SessionPromptKind,
|
|
455
|
+
opts: SessionSystemPromptOpts,
|
|
456
|
+
sessionTemplate: string,
|
|
457
|
+
): string => {
|
|
458
|
+
const context = Object.freeze({ ...opts, sessionKind });
|
|
459
|
+
const render = (slot: PromptSectionSlot): string[] =>
|
|
460
|
+
sections
|
|
461
|
+
.filter((section) => section.slot === slot)
|
|
462
|
+
.map((section) => section.render(context)?.trim())
|
|
463
|
+
.filter((section): section is string => Boolean(section))
|
|
464
|
+
.flatMap((section) => ["", section]);
|
|
465
|
+
const uploadsDir = `${opts.workspaceCwd}/attachments`;
|
|
466
|
+
let prompt =
|
|
467
|
+
replaceVars(sessionTemplate, {
|
|
468
|
+
AI_LABEL: opts.aiLabel,
|
|
469
|
+
TOPIC_TITLE: opts.topicTitle,
|
|
470
|
+
WORKSPACE_CWD: opts.workspaceCwd,
|
|
471
|
+
UPLOADS_DIR: uploadsDir,
|
|
472
|
+
}) +
|
|
473
|
+
buildRuntimeToolSection(
|
|
474
|
+
{
|
|
475
|
+
agentKind: opts.agentKind,
|
|
476
|
+
canSpawnSubagents: sessionKind === "channel" ? false : opts.canSpawnSubagents,
|
|
477
|
+
canStageSubagents: sessionKind === "channel" ? false : opts.canStageSubagents,
|
|
478
|
+
visualTools: opts.visualTools,
|
|
479
|
+
fileDeliveryTools: opts.fileDeliveryTools,
|
|
480
|
+
currentModel: opts.currentModel,
|
|
481
|
+
currentEffort: opts.currentEffort,
|
|
482
|
+
subagentParentTitle: opts.subagentParentTitle,
|
|
483
|
+
subagentReportMode: opts.subagentReportMode,
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
render,
|
|
487
|
+
visualDesignGuide: template({
|
|
488
|
+
kind: "visual-design",
|
|
489
|
+
filename: "visual-design.md",
|
|
490
|
+
fallback: "",
|
|
491
|
+
}),
|
|
492
|
+
},
|
|
493
|
+
);
|
|
494
|
+
if (sessionKind !== "channel" && opts.description?.trim()) {
|
|
495
|
+
prompt += `\n\n## Topic-Specific Instructions\n${opts.description.trim()}`;
|
|
496
|
+
}
|
|
497
|
+
if (sessionKind === "manager") {
|
|
498
|
+
prompt += `\n\n${template({
|
|
499
|
+
kind: "manager-system",
|
|
500
|
+
filename: "manager-system.md",
|
|
501
|
+
fallback: FALLBACK_MANAGER_SYSTEM_PROMPT_TEMPLATE,
|
|
502
|
+
})}`;
|
|
503
|
+
}
|
|
504
|
+
return `${prompt}${render("after-system-prompt").join("\n")}`;
|
|
505
|
+
};
|
|
404
506
|
|
|
405
|
-
|
|
406
|
-
|
|
507
|
+
return Object.freeze({
|
|
508
|
+
buildTopicSystemPrompt(opts: SessionSystemPromptOpts) {
|
|
509
|
+
return build(
|
|
510
|
+
"topic",
|
|
511
|
+
opts,
|
|
512
|
+
template({
|
|
513
|
+
kind: "topic-system",
|
|
514
|
+
filename: "topic-system.md",
|
|
515
|
+
fallback: FALLBACK_TOPIC_SYSTEM_PROMPT_TEMPLATE,
|
|
516
|
+
}),
|
|
517
|
+
);
|
|
518
|
+
},
|
|
519
|
+
buildChannelSystemPrompt(opts: SessionSystemPromptOpts) {
|
|
520
|
+
return build(
|
|
521
|
+
"channel",
|
|
522
|
+
opts,
|
|
523
|
+
template({
|
|
524
|
+
kind: "channel-system",
|
|
525
|
+
filename: "channel-system.md",
|
|
526
|
+
fallback: FALLBACK_CHANNEL_SYSTEM_PROMPT_TEMPLATE,
|
|
527
|
+
}),
|
|
528
|
+
);
|
|
529
|
+
},
|
|
530
|
+
buildManagerSystemPrompt(opts: SessionSystemPromptOpts) {
|
|
531
|
+
return build(
|
|
532
|
+
"manager",
|
|
533
|
+
opts,
|
|
534
|
+
template({
|
|
535
|
+
kind: "topic-system",
|
|
536
|
+
filename: "topic-system.md",
|
|
537
|
+
fallback: FALLBACK_TOPIC_SYSTEM_PROMPT_TEMPLATE,
|
|
538
|
+
}),
|
|
539
|
+
);
|
|
540
|
+
},
|
|
541
|
+
});
|
|
407
542
|
}
|
|
408
543
|
|
|
544
|
+
const defaultPromptBuilders = createPromptBuilders();
|
|
545
|
+
|
|
546
|
+
export const buildTopicSystemPrompt = defaultPromptBuilders.buildTopicSystemPrompt;
|
|
547
|
+
export const buildChannelSystemPrompt = defaultPromptBuilders.buildChannelSystemPrompt;
|
|
548
|
+
export const buildManagerSystemPrompt = defaultPromptBuilders.buildManagerSystemPrompt;
|
|
549
|
+
|
|
409
550
|
export function buildMemoryPromptSection(opts: {
|
|
410
551
|
briefFile: string;
|
|
411
552
|
wikiDir: string;
|
|
@@ -17,6 +17,14 @@ export {
|
|
|
17
17
|
type RoomQueryRegistryHost,
|
|
18
18
|
type RuntimeTurnLeaseLike,
|
|
19
19
|
} from "./room-query-registry";
|
|
20
|
+
export {
|
|
21
|
+
clearQueryState,
|
|
22
|
+
createQueryStateStore,
|
|
23
|
+
type QueryStateStore,
|
|
24
|
+
type QueryStateStoreLogger,
|
|
25
|
+
type QueryStateStoreOptions,
|
|
26
|
+
writeQueryState,
|
|
27
|
+
} from "./state";
|
|
20
28
|
export {
|
|
21
29
|
AbortReason,
|
|
22
30
|
type HandleAgentQueryOutcome,
|
|
@@ -7,38 +7,86 @@ import type { QueryState } from "#types";
|
|
|
7
7
|
|
|
8
8
|
type QueryStateUserId = number | string;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
export interface QueryStateStoreLogger {
|
|
11
|
+
warn(context: Record<string, unknown>, message: string): void;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
export interface QueryStateStoreOptions {
|
|
15
|
+
usersLogDir: string;
|
|
16
|
+
logger?: QueryStateStoreLogger;
|
|
17
|
+
sanitizeTopicId?: (topicId: string) => string;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
userId: QueryStateUserId,
|
|
20
|
-
|
|
21
|
-
): string | null {
|
|
22
|
-
if (!topicName || topicName === "." || topicName === ".." || basename(topicName) !== topicName) {
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
return join(queryStateDirPath(userId), `${topicName}.json`);
|
|
20
|
+
export interface QueryStateStore {
|
|
21
|
+
write(userId: QueryStateUserId, topicId: string, topicName: string, task?: string): void;
|
|
22
|
+
clear(userId: QueryStateUserId, topicId: string, legacyTopicName?: string): void;
|
|
26
23
|
}
|
|
27
24
|
|
|
25
|
+
export function createQueryStateStore(options: QueryStateStoreOptions): QueryStateStore {
|
|
26
|
+
const sanitize = options.sanitizeTopicId ?? sanitizeId;
|
|
27
|
+
const queryStateDirPath = (userId: QueryStateUserId): string =>
|
|
28
|
+
join(options.usersLogDir, String(userId), "active-queries");
|
|
29
|
+
const queryStateFile = (userId: QueryStateUserId, topicId: string): string =>
|
|
30
|
+
join(queryStateDirPath(userId), `${sanitize(topicId)}.json`);
|
|
31
|
+
const legacyQueryStateFile = (
|
|
32
|
+
userId: QueryStateUserId,
|
|
33
|
+
topicName: string | undefined,
|
|
34
|
+
): string | null => {
|
|
35
|
+
if (
|
|
36
|
+
!topicName ||
|
|
37
|
+
topicName === "." ||
|
|
38
|
+
topicName === ".." ||
|
|
39
|
+
basename(topicName) !== topicName
|
|
40
|
+
) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return join(queryStateDirPath(userId), `${topicName}.json`);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
write(userId, topicId, topicName, task) {
|
|
48
|
+
const dir = queryStateDirPath(userId);
|
|
49
|
+
mkdirSync(dir, { recursive: true });
|
|
50
|
+
const state: QueryState = { topicId, topicName, since: new Date().toISOString() };
|
|
51
|
+
if (task) state.task = [...task.replace(/\n+/g, " ").trim()].slice(0, 100).join("");
|
|
52
|
+
const target = queryStateFile(userId, topicId);
|
|
53
|
+
const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
54
|
+
writeFileSync(tmp, JSON.stringify(state));
|
|
55
|
+
renameSync(tmp, target);
|
|
56
|
+
},
|
|
57
|
+
clear(userId, topicId, legacyTopicName) {
|
|
58
|
+
const paths = [
|
|
59
|
+
queryStateFile(userId, topicId),
|
|
60
|
+
legacyQueryStateFile(userId, legacyTopicName),
|
|
61
|
+
].filter((path): path is string => Boolean(path));
|
|
62
|
+
for (const path of new Set(paths)) {
|
|
63
|
+
try {
|
|
64
|
+
unlinkSync(path);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if ((error as NodeJS.ErrnoException)?.code !== "ENOENT") {
|
|
67
|
+
options.logger?.warn(
|
|
68
|
+
{ err: error, userId, topicId, path },
|
|
69
|
+
"Failed to clear query state file",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const defaultQueryStateStore = createQueryStateStore({
|
|
79
|
+
usersLogDir: USERS_LOG_DIR,
|
|
80
|
+
logger,
|
|
81
|
+
});
|
|
82
|
+
|
|
28
83
|
export function writeQueryState(
|
|
29
84
|
userId: QueryStateUserId,
|
|
30
85
|
topicId: string,
|
|
31
86
|
topicName: string,
|
|
32
87
|
task?: string,
|
|
33
88
|
) {
|
|
34
|
-
|
|
35
|
-
mkdirSync(dir, { recursive: true });
|
|
36
|
-
const state: QueryState = { topicId, topicName, since: new Date().toISOString() };
|
|
37
|
-
if (task) state.task = [...task.replace(/\n+/g, " ").trim()].slice(0, 100).join("");
|
|
38
|
-
const target = queryStateFile(userId, topicId);
|
|
39
|
-
const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
40
|
-
writeFileSync(tmp, JSON.stringify(state));
|
|
41
|
-
renameSync(tmp, target);
|
|
89
|
+
defaultQueryStateStore.write(userId, topicId, topicName, task);
|
|
42
90
|
}
|
|
43
91
|
|
|
44
92
|
export function clearQueryState(
|
|
@@ -46,17 +94,5 @@ export function clearQueryState(
|
|
|
46
94
|
topicId: string,
|
|
47
95
|
legacyTopicName?: string,
|
|
48
96
|
) {
|
|
49
|
-
|
|
50
|
-
queryStateFile(userId, topicId),
|
|
51
|
-
legacyQueryStateFile(userId, legacyTopicName),
|
|
52
|
-
].filter((path): path is string => Boolean(path));
|
|
53
|
-
for (const path of new Set(paths)) {
|
|
54
|
-
try {
|
|
55
|
-
unlinkSync(path);
|
|
56
|
-
} catch (e) {
|
|
57
|
-
if ((e as NodeJS.ErrnoException)?.code !== "ENOENT") {
|
|
58
|
-
logger.warn({ err: e, userId, topicId, path }, "Failed to clear query state file");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
97
|
+
defaultQueryStateStore.clear(userId, topicId, legacyTopicName);
|
|
62
98
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const NEGOTIUM_VERSION = "0.1.
|
|
1
|
+
export const NEGOTIUM_VERSION = "0.1.39";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type TopicConfig } from "../storage/api-topic-config";
|
|
2
|
+
import type { AgentKind, EffortLevel } from "../types";
|
|
3
|
+
export type ApiTopicSwitchOutcome = {
|
|
4
|
+
kind: "fresh";
|
|
5
|
+
agent: AgentKind;
|
|
6
|
+
reason: "no-history" | "bridge-failed";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "bridged";
|
|
9
|
+
agent: AgentKind;
|
|
10
|
+
bridgedSessionId: string;
|
|
11
|
+
rolloutPath: string;
|
|
12
|
+
};
|
|
13
|
+
export type ApiTopicSwitchResult = {
|
|
14
|
+
ok: true;
|
|
15
|
+
outcome: ApiTopicSwitchOutcome;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
export interface SwitchApiTopicAgentOptions {
|
|
21
|
+
topicId: string;
|
|
22
|
+
topicTitle: string;
|
|
23
|
+
userId: string;
|
|
24
|
+
fromAgent?: AgentKind;
|
|
25
|
+
agent: AgentKind;
|
|
26
|
+
cwd: string;
|
|
27
|
+
config: TopicConfig;
|
|
28
|
+
/** Topic base values used when config has no explicit override. */
|
|
29
|
+
defaultModel?: string;
|
|
30
|
+
defaultEffort?: EffortLevel;
|
|
31
|
+
reason: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* API-topic equivalent of `switchTopicAgent`: all web/MCP agent changes must
|
|
35
|
+
* pass through this path so the synthetic SDK rollout, unified-log manifest,
|
|
36
|
+
* and durable topic session stay in sync.
|
|
37
|
+
*/
|
|
38
|
+
export declare function switchApiTopicAgent(opts: SwitchApiTopicAgentOptions): ApiTopicSwitchResult;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type {
|
|
3
|
-
|
|
1
|
+
import { type AgentDef } from "../prompts/builders";
|
|
2
|
+
import type { AgentKind, AgentQueryOptions, UnifiedEvent } from "../types";
|
|
3
|
+
import type { BackgroundSessionDto, MessageDto } from "../types/api";
|
|
4
4
|
export interface RunArchiverTurnParams {
|
|
5
5
|
/** Stringified user id (route layer hands us strings). */
|
|
6
6
|
userId: string;
|
|
@@ -23,17 +23,53 @@ export interface RunArchiverTurnParams {
|
|
|
23
23
|
/** Called once when an accepted background turn settles. */
|
|
24
24
|
onSettled?: (success: boolean) => void;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
export interface ArchiverStorageHost {
|
|
27
|
+
getWikiDir(): string;
|
|
28
|
+
fileExists(path: string): boolean;
|
|
29
|
+
listDirectory(path: string): string[];
|
|
30
|
+
readTextFile(path: string): string;
|
|
31
|
+
fileSize(path: string): number;
|
|
32
|
+
fileModifiedAt(path: string): number;
|
|
33
|
+
getGeneralTopicId(userId: string): string;
|
|
34
|
+
getTopicBrief(topicId: string): {
|
|
35
|
+
briefMd: string;
|
|
36
|
+
} | null;
|
|
37
|
+
setTopicBrief(topicId: string, fields: {
|
|
38
|
+
briefMd: string;
|
|
39
|
+
latestSummaryMd?: string;
|
|
40
|
+
summaryDate?: string;
|
|
41
|
+
}): void;
|
|
42
|
+
}
|
|
43
|
+
export interface ArchiverMessagingHost {
|
|
44
|
+
appendMessage(message: MessageDto): void;
|
|
45
|
+
broadcastMessage(topicId: string, message: MessageDto): void;
|
|
46
|
+
}
|
|
47
|
+
export interface ArchiverConfigHost {
|
|
48
|
+
readonly workspaceDir: string;
|
|
49
|
+
readonly completedSessionRetentionMs: number;
|
|
50
|
+
loadAgentDefinition(): AgentDef;
|
|
51
|
+
sanitizeTopicName(topicTitle: string): string;
|
|
52
|
+
createId(): string;
|
|
53
|
+
now(): Date;
|
|
54
|
+
schedule(callback: () => void, delayMs: number): unknown;
|
|
55
|
+
cancelScheduled(handle: unknown): void;
|
|
56
|
+
unrefScheduled?(handle: unknown): void;
|
|
57
|
+
info(context: Record<string, unknown>, message: string): void;
|
|
58
|
+
warn(context: Record<string, unknown>, message: string): void;
|
|
59
|
+
}
|
|
60
|
+
export interface ArchiverAgentRuntimeHost {
|
|
61
|
+
run(options: AgentQueryOptions): AsyncIterable<UnifiedEvent>;
|
|
62
|
+
}
|
|
63
|
+
export interface ArchiverHost {
|
|
64
|
+
storage: ArchiverStorageHost;
|
|
65
|
+
messaging: ArchiverMessagingHost;
|
|
66
|
+
config: ArchiverConfigHost;
|
|
67
|
+
agentRuntime: ArchiverAgentRuntimeHost;
|
|
68
|
+
}
|
|
69
|
+
export interface ArchiverRuntime {
|
|
70
|
+
runArchiverTurn(params: RunArchiverTurnParams): boolean;
|
|
71
|
+
listActiveMemoryArchiverSessions(userId: string): BackgroundSessionDto[];
|
|
72
|
+
}
|
|
73
|
+
export declare function createArchiverRuntime(host: ArchiverHost): ArchiverRuntime;
|
|
39
74
|
export declare function runArchiverTurn(params: RunArchiverTurnParams): boolean;
|
|
75
|
+
export declare function listActiveMemoryArchiverSessions(userId: string): BackgroundSessionDto[];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type SharedMcpTool } from "./common";
|
|
2
|
+
import { type AskUserGateCardUpdate, type ClaimAskUserGateResult, type PrepareAskUserGateResult } from "../../storage/ask-user-gates";
|
|
3
|
+
import { type RuntimeProcessLease, type RuntimeProcessLeaseHandle } from "../../storage/runtime-process-leases";
|
|
2
4
|
import type { AgentKind } from "../../types";
|
|
3
|
-
import type { MessageDto } from "../../types/api";
|
|
5
|
+
import type { MessageDto, TopicDto } from "../../types/api";
|
|
4
6
|
export type AskUserChoice = {
|
|
5
7
|
label: string;
|
|
6
8
|
description?: string;
|
|
@@ -20,8 +22,70 @@ export type AnswerAskUserQuestionResult = {
|
|
|
20
22
|
ok: false;
|
|
21
23
|
error: string;
|
|
22
24
|
};
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
+
export interface AskUserRuntimeHost {
|
|
26
|
+
messaging: {
|
|
27
|
+
persistence: {
|
|
28
|
+
getMessage(topicId: string, messageId: string): MessageDto | null | undefined;
|
|
29
|
+
appendMessage(message: MessageDto): void;
|
|
30
|
+
};
|
|
31
|
+
publication: {
|
|
32
|
+
publishMessage(topicId: string, message: MessageDto): void;
|
|
33
|
+
publishCardUpdate(update: AskUserGateCardUpdate): void;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
topics: {
|
|
37
|
+
getTopic(topicId: string): Pick<TopicDto, "participants"> | null | undefined;
|
|
38
|
+
getConfig(topicId: string): {
|
|
39
|
+
model?: string;
|
|
40
|
+
} | undefined;
|
|
41
|
+
};
|
|
42
|
+
durability: {
|
|
43
|
+
gates: {
|
|
44
|
+
prepare(args: {
|
|
45
|
+
gateId: string;
|
|
46
|
+
topicId: string;
|
|
47
|
+
queryId?: string;
|
|
48
|
+
idempotencyKey: string;
|
|
49
|
+
bodyHash: string;
|
|
50
|
+
messageId: string;
|
|
51
|
+
ownerId: string;
|
|
52
|
+
now: string;
|
|
53
|
+
}): PrepareAskUserGateResult;
|
|
54
|
+
claimAndSelect(args: {
|
|
55
|
+
topicId: string;
|
|
56
|
+
messageId: string;
|
|
57
|
+
label: string;
|
|
58
|
+
userId: string;
|
|
59
|
+
ownerId: string;
|
|
60
|
+
source: string;
|
|
61
|
+
now: string;
|
|
62
|
+
}): ClaimAskUserGateResult;
|
|
63
|
+
cancel(topicId: string, messageId: string, ownerId: string, now: string): AskUserGateCardUpdate | null;
|
|
64
|
+
quarantine(gateId: string, ownerId: string, now: string): AskUserGateCardUpdate | null;
|
|
65
|
+
quarantineForeign(liveOwnerIds: ReadonlySet<string>, now: string): AskUserGateCardUpdate[];
|
|
66
|
+
};
|
|
67
|
+
processLeases: {
|
|
68
|
+
acquire(role: string, ownerId: string): RuntimeProcessLeaseHandle | null;
|
|
69
|
+
list(rolePrefix: string): RuntimeProcessLease[];
|
|
70
|
+
isAlive(lease: RuntimeProcessLease): boolean;
|
|
71
|
+
removeDead(rolePrefix: string): void;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
runtime: {
|
|
75
|
+
ownerId: string;
|
|
76
|
+
createId(): string;
|
|
77
|
+
now(): string;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export interface AskUserRuntime {
|
|
81
|
+
startGateOwner(): void;
|
|
82
|
+
stopGateOwner(): void;
|
|
83
|
+
reconcilePendingGates(): number;
|
|
84
|
+
hasPendingQuestion(topicId: string, messageId: string): boolean;
|
|
85
|
+
answerPendingQuestion(topicId: string, messageId: string, label: string, userId: string): AnswerAskUserQuestionResult;
|
|
86
|
+
cancelPendingQuestions(topicId: string, queryId: string): void;
|
|
87
|
+
createToolDefinition(ctx: AskUserToolContext): SharedMcpTool;
|
|
88
|
+
}
|
|
25
89
|
export declare function normalizeAskUserChoices(value: unknown): AskUserChoice[];
|
|
26
90
|
export declare function normalizeAskUserQuestionInput(input: {
|
|
27
91
|
question?: unknown;
|
|
@@ -32,8 +96,15 @@ export declare function normalizeAskUserQuestionInput(input: {
|
|
|
32
96
|
} | {
|
|
33
97
|
error: string;
|
|
34
98
|
};
|
|
35
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Create an isolated ask-user lifecycle over host-provided messaging, topic,
|
|
101
|
+
* durability, and runtime services.
|
|
102
|
+
*/
|
|
103
|
+
export declare function createAskUserRuntime(host: AskUserRuntimeHost): AskUserRuntime;
|
|
104
|
+
export declare function startAskUserQuestionGateOwner(): void;
|
|
105
|
+
export declare function stopAskUserQuestionGateOwner(): void;
|
|
36
106
|
export declare function reconcilePendingAskUserQuestionGates(): number;
|
|
107
|
+
export declare function hasPendingAskUserQuestion(topicId: string, messageId: string): boolean;
|
|
37
108
|
/**
|
|
38
109
|
* Host-neutral answer path for runtime ask cards. Web, terminal, Telegram, and
|
|
39
110
|
* future adapters should call this rather than reimplementing pending-map and
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type SharedMcpTool } from "./common";
|
|
2
|
+
import { SELF_CONFIG_MCP_KEY, type SelfConfigContext, type SelfConfigCore, type SelfConfigHost, type SelfConfigProductConfig } from "../self-config-core";
|
|
3
|
+
export type { SelfConfigContext };
|
|
4
|
+
export { SELF_CONFIG_MCP_KEY };
|
|
5
|
+
export declare const SELF_CONFIG_DERIVED_TOPIC_LIMIT = 5;
|
|
6
|
+
export interface SelfConfigRuntime {
|
|
7
|
+
readonly core: SelfConfigCore;
|
|
8
|
+
readonly mcpKey: string;
|
|
9
|
+
createToolDefinitions(ctx: SelfConfigContext | (() => SelfConfigContext)): SharedMcpTool[];
|
|
10
|
+
}
|
|
11
|
+
export interface SelfConfigRuntimeOptions {
|
|
12
|
+
host: SelfConfigHost;
|
|
13
|
+
product?: Partial<SelfConfigProductConfig>;
|
|
14
|
+
}
|
|
15
|
+
export declare function createSelfConfigRuntime(options: SelfConfigRuntimeOptions): SelfConfigRuntime;
|
|
16
|
+
export declare function createSelfConfigToolDefinitionsForCore(core: SelfConfigCore, ctx: SelfConfigContext | (() => SelfConfigContext)): SharedMcpTool[];
|
|
17
|
+
export declare function createSelfConfigToolDefinitions(ctx: SelfConfigContext | (() => SelfConfigContext)): SharedMcpTool[];
|