negotium 0.3.6 → 0.3.7

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/dist/main.js CHANGED
@@ -1869,7 +1869,7 @@ var exports_version = {};
1869
1869
  __export(exports_version, {
1870
1870
  NEGOTIUM_VERSION: () => NEGOTIUM_VERSION
1871
1871
  });
1872
- var NEGOTIUM_VERSION = "0.3.6";
1872
+ var NEGOTIUM_VERSION = "0.3.7";
1873
1873
 
1874
1874
  // ../../packages/core/src/agents/codex-native-multi-agent.ts
1875
1875
  import { spawn } from "child_process";
@@ -20498,12 +20498,14 @@ function gatewayPayloadHash(params, requestId, actorUserId) {
20498
20498
  params.clientMessageId,
20499
20499
  requestId,
20500
20500
  params.allowAutoContinue ?? true,
20501
+ params.respond ?? true,
20501
20502
  params.threadRootId ?? null
20502
20503
  ])).digest("hex");
20503
20504
  }
20504
20505
  function submitRuntimeGatewayTurn(params) {
20505
20506
  const requestId = params.requestId ?? params.clientMessageId;
20506
20507
  const actorUserId = params.actorUserId ?? params.userId;
20508
+ const respond = params.respond ?? true;
20507
20509
  const payloadHash = gatewayPayloadHash(params, requestId, actorUserId);
20508
20510
  const existing = findRuntimeGatewaySubmission(params.clientMessageId, requestId);
20509
20511
  if (existing) {
@@ -20535,28 +20537,30 @@ function submitRuntimeGatewayTurn(params) {
20535
20537
  try {
20536
20538
  db.transaction(() => {
20537
20539
  appendApiMessage(message, { notify: false });
20538
- mergeRuntimeUserTurnRequest({
20539
- topicId: params.topic.id,
20540
- userId: params.userId,
20541
- userMessages: [
20542
- {
20543
- prompt: params.text,
20544
- actorUserId,
20545
- ...params.actorLabel ? { actorLabel: params.actorLabel } : {}
20540
+ if (respond) {
20541
+ mergeRuntimeUserTurnRequest({
20542
+ topicId: params.topic.id,
20543
+ userId: params.userId,
20544
+ userMessages: [
20545
+ {
20546
+ prompt: params.text,
20547
+ actorUserId,
20548
+ ...params.actorLabel ? { actorLabel: params.actorLabel } : {}
20549
+ }
20550
+ ],
20551
+ allowAutoContinue: params.allowAutoContinue ?? true,
20552
+ requestId,
20553
+ topicEpoch: getRuntimeTopicEpoch(params.topic.id),
20554
+ execution: {
20555
+ sessionId: getTopicSessionId(params.topic.id),
20556
+ sessionIdSpecified: true,
20557
+ conversationPrompts: [params.text],
20558
+ loggedUserMessageCount: 0,
20559
+ vaultUserId: params.vaultUserId,
20560
+ ...params.threadRootId ? { threadRootId: params.threadRootId } : {}
20546
20561
  }
20547
- ],
20548
- allowAutoContinue: params.allowAutoContinue ?? true,
20549
- requestId,
20550
- topicEpoch: getRuntimeTopicEpoch(params.topic.id),
20551
- execution: {
20552
- sessionId: getTopicSessionId(params.topic.id),
20553
- sessionIdSpecified: true,
20554
- conversationPrompts: [params.text],
20555
- loggedUserMessageCount: 0,
20556
- vaultUserId: params.vaultUserId,
20557
- ...params.threadRootId ? { threadRootId: params.threadRootId } : {}
20558
- }
20559
- });
20562
+ });
20563
+ }
20560
20564
  const acceptedEvent = appendRuntimeEvent("runtime-gateway-ingress", {
20561
20565
  type: "ai-status",
20562
20566
  topicId: params.topic.id,
@@ -20582,7 +20586,8 @@ function submitRuntimeGatewayTurn(params) {
20582
20586
  return duplicateResult(raced, params, requestId, actorUserId, payloadHash);
20583
20587
  throw new Error("failed to persist gateway turn idempotency record");
20584
20588
  }
20585
- requestRuntimeTurnAbort(params.topic.id, "internal");
20589
+ if (respond)
20590
+ requestRuntimeTurnAbort(params.topic.id, "internal");
20586
20591
  return { ...submission, message, deduplicated: false };
20587
20592
  }
20588
20593
  var RuntimeGatewayIdempotencyConflictError;
@@ -22622,6 +22627,102 @@ var init_inbox = __esm(async () => {
22622
22627
  topicWorkerBusy = new Set;
22623
22628
  });
22624
22629
 
22630
+ // ../../packages/core/src/topics/update.ts
22631
+ function updateTopicSettings(opts) {
22632
+ const current3 = getTopic(opts.topicId);
22633
+ if (!current3)
22634
+ throw new TopicValidationError("Topic not found");
22635
+ if (current3.kind === "manager") {
22636
+ throw new TopicValidationError("Manager rooms are system-managed");
22637
+ }
22638
+ let title = current3.title;
22639
+ if (opts.title !== undefined) {
22640
+ title = opts.title.trim();
22641
+ if (!title)
22642
+ throw new TopicValidationError("title is required");
22643
+ if (RESERVED_TOPIC_NAMES.has(title.toLowerCase())) {
22644
+ throw new TopicValidationError(`"${title}" is a reserved name`);
22645
+ }
22646
+ }
22647
+ if (opts.agent !== undefined && opts.agent !== null && !isAgentKind(opts.agent)) {
22648
+ throw new TopicValidationError(`Unknown agent '${opts.agent}'`);
22649
+ }
22650
+ const requestedAgent = opts.agent === undefined ? current3.agent : opts.agent ?? undefined;
22651
+ const requestedAiMode = opts.aiMode ?? current3.aiMode;
22652
+ const requestedKind = opts.agent === null || requestedAiMode === "off" || requestedAiMode === "mention" ? "channel" : normalizeTopicKind(current3.kind);
22653
+ const { kind, aiMode, agent } = normalizeTopicState({
22654
+ id: current3.id,
22655
+ kind: requestedKind,
22656
+ agent: requestedAgent,
22657
+ aiMode: normalizeAiMode(requestedAiMode)
22658
+ });
22659
+ if (title.toLowerCase() !== current3.title.toLowerCase() || kind !== current3.kind) {
22660
+ const conflict = findTopicTitleConflict(title, kind, {
22661
+ excludeTopicId: current3.id,
22662
+ surface: current3.surface,
22663
+ surfaceScope: current3.surfaceScope ?? null
22664
+ });
22665
+ if (conflict) {
22666
+ throw new TopicUpdateConflictError(`A topic named "${title}" already exists on ${current3.surface ?? "this surface"}`);
22667
+ }
22668
+ }
22669
+ const registry = getRegistry(agent ?? "maestro");
22670
+ if (opts.defaultModel !== undefined) {
22671
+ if (!agent)
22672
+ throw new TopicValidationError("This topic has no AI model");
22673
+ if (!registry.validateModel(opts.defaultModel)) {
22674
+ throw new TopicValidationError(`model '${opts.defaultModel}' is not valid for agent '${agent}'`);
22675
+ }
22676
+ }
22677
+ if (opts.defaultEffort !== undefined) {
22678
+ if (!agent)
22679
+ throw new TopicValidationError("This topic has no AI effort");
22680
+ if (!registry.validateEffort(opts.defaultEffort)) {
22681
+ throw new TopicValidationError(`effort '${opts.defaultEffort}' is not valid for agent '${agent}'`);
22682
+ }
22683
+ }
22684
+ const defaultModel = resolveModelForAgent(agent ?? "maestro", opts.defaultModel ?? (agent === current3.agent ? current3.defaultModel : undefined), registry);
22685
+ const requestedEffort = opts.defaultEffort ?? (agent === current3.agent ? current3.defaultEffort : undefined);
22686
+ const defaultEffort = requestedEffort && registry.validateEffort(requestedEffort) ? requestedEffort : registry.defaultEffort ?? "medium";
22687
+ const next = {
22688
+ ...current3,
22689
+ title,
22690
+ kind,
22691
+ aiMode,
22692
+ agent,
22693
+ defaultModel,
22694
+ defaultEffort
22695
+ };
22696
+ upsertTopic(next);
22697
+ const config = getApiTopicConfig(next.id);
22698
+ if (config && (opts.defaultModel !== undefined || opts.defaultEffort !== undefined)) {
22699
+ setApiTopicConfig(next.id, {
22700
+ ...config,
22701
+ ...opts.defaultModel !== undefined ? { model: undefined, modelLocked: undefined } : {},
22702
+ ...opts.defaultEffort !== undefined ? { effort: undefined, effortLocked: undefined } : {}
22703
+ });
22704
+ }
22705
+ WsHub.get().broadcastTopicUpdated(next.id);
22706
+ return getTopic(next.id) ?? next;
22707
+ }
22708
+ var TopicUpdateConflictError;
22709
+ var init_update = __esm(async () => {
22710
+ init_model_catalog();
22711
+ await init_registry();
22712
+ await init_bus();
22713
+ init_constants();
22714
+ await init_api_topic_config();
22715
+ await init_api_topics();
22716
+ await init_create();
22717
+ init_types();
22718
+ TopicUpdateConflictError = class TopicUpdateConflictError extends Error {
22719
+ constructor(message) {
22720
+ super(message);
22721
+ this.name = "TopicUpdateConflictError";
22722
+ }
22723
+ };
22724
+ });
22725
+
22625
22726
  // ../../packages/core/src/index.ts
22626
22727
  var exports_src = {};
22627
22728
  __export(exports_src, {
@@ -22635,6 +22736,7 @@ __export(exports_src, {
22635
22736
  vaultDel: () => vaultDel,
22636
22737
  validateVaultKey: () => validateVaultKey,
22637
22738
  upsertTopic: () => upsertTopic,
22739
+ updateTopicSettings: () => updateTopicSettings,
22638
22740
  updateTopic: () => updateTopic,
22639
22741
  unregisterNodeRequestHandler: () => unregisterNodeRequestHandler,
22640
22742
  triggerTopicAiTurn: () => triggerTopicAiTurn,
@@ -22825,6 +22927,7 @@ __export(exports_src, {
22825
22927
  VAULT_DESCRIPTION_MAX_LENGTH: () => VAULT_DESCRIPTION_MAX_LENGTH,
22826
22928
  VAULT_COMMAND_HELP: () => VAULT_COMMAND_HELP,
22827
22929
  TopicValidationError: () => TopicValidationError,
22930
+ TopicUpdateConflictError: () => TopicUpdateConflictError,
22828
22931
  TopicTurnStillActiveError: () => TopicTurnStillActiveError,
22829
22932
  TopicTitleConflictError: () => TopicTitleConflictError,
22830
22933
  TopicServiceError: () => TopicServiceError,
@@ -22924,6 +23027,7 @@ var init_src = __esm(async () => {
22924
23027
  await init_lifecycle();
22925
23028
  await init_personal_general();
22926
23029
  await init_session();
23030
+ await init_update();
22927
23031
  init_types();
22928
23032
  init_types();
22929
23033
  });
@@ -23243,6 +23347,7 @@ var exports_node_host = {};
23243
23347
  __export(exports_node_host, {
23244
23348
  writeDecisionGraphSvg: () => writeDecisionGraphSvg,
23245
23349
  upsertTopic: () => upsertTopic,
23350
+ updateTopicSettings: () => updateTopicSettings,
23246
23351
  topicService: () => topicService,
23247
23352
  switchTopicModel: () => switchTopicModel,
23248
23353
  switchTopicEffort: () => switchTopicEffort,
@@ -23297,6 +23402,8 @@ __export(exports_node_host, {
23297
23402
  abortAllRooms: () => abortAllRooms,
23298
23403
  WsHub: () => WsHub,
23299
23404
  WORKSPACE_DIR: () => WORKSPACE_DIR,
23405
+ TopicValidationError: () => TopicValidationError,
23406
+ TopicUpdateConflictError: () => TopicUpdateConflictError,
23300
23407
  TopicTitleConflictError: () => TopicTitleConflictError,
23301
23408
  TopicServiceError: () => TopicServiceError,
23302
23409
  TopicForkCompactionError: () => TopicForkCompactionError,
@@ -23343,9 +23450,11 @@ var init_node_host = __esm(async () => {
23343
23450
  await init_runtime_events();
23344
23451
  await init_runtime_process_leases();
23345
23452
  await init_token_stats();
23453
+ await init_create();
23346
23454
  await init_derive();
23347
23455
  await init_personal_general();
23348
23456
  await init_session();
23457
+ await init_update();
23349
23458
  });
23350
23459
 
23351
23460
  // ../../packages/core/src/agents/mcp-tools/inline-assets.ts
@@ -30096,7 +30205,13 @@ function createNodeControlHandler(options) {
30096
30205
  "canonical-message-read",
30097
30206
  "canonical-topic-list",
30098
30207
  "canonical-topic-create",
30099
- "canonical-history-import"
30208
+ "canonical-topic-update",
30209
+ "canonical-topic-delete",
30210
+ "turn-submit-silent",
30211
+ "canonical-history-import",
30212
+ "canonical-topic-abort",
30213
+ "canonical-session-reset",
30214
+ "canonical-session-compact"
30100
30215
  ],
30101
30216
  cursor: latestRuntimeEventSeq()
30102
30217
  });
@@ -30140,6 +30255,7 @@ function createNodeControlHandler(options) {
30140
30255
  clientMessageId,
30141
30256
  requestId,
30142
30257
  allowAutoContinue: body.allowAutoContinue !== false,
30258
+ respond: body.respond !== false,
30143
30259
  ...threadRootId ? { threadRootId } : {}
30144
30260
  });
30145
30261
  return Response.json({
@@ -30252,6 +30368,71 @@ function createNodeControlHandler(options) {
30252
30368
  imported
30253
30369
  });
30254
30370
  }
30371
+ const runtimeAbortMatch = runtimePath.match(/^\/topics\/([^/]+)\/abort$/);
30372
+ if (runtimeAbortMatch && req.method === "POST") {
30373
+ const body = await bodyRecord(req);
30374
+ if (body.v !== NODE_RUNTIME_CONTRACT_VERSION)
30375
+ return jsonError(400, "Unsupported v");
30376
+ const topicId = decodeURIComponent(runtimeAbortMatch[1]);
30377
+ const topic = getTopic(topicId);
30378
+ if (!topic || !topicInRequestScope(req, topic))
30379
+ return jsonError(404, "Topic not found");
30380
+ const userId = requiredText(body.userId, "userId");
30381
+ return Response.json({
30382
+ ok: true,
30383
+ v: NODE_RUNTIME_CONTRACT_VERSION,
30384
+ aborted: topicService.abortTurn(topicId, userId)
30385
+ });
30386
+ }
30387
+ const runtimeResetMatch = runtimePath.match(/^\/topics\/([^/]+)\/session\/reset$/);
30388
+ if (runtimeResetMatch && req.method === "POST") {
30389
+ const body = await bodyRecord(req);
30390
+ if (body.v !== NODE_RUNTIME_CONTRACT_VERSION)
30391
+ return jsonError(400, "Unsupported v");
30392
+ const topicId = decodeURIComponent(runtimeResetMatch[1]);
30393
+ const topic = getTopic(topicId);
30394
+ if (!topic || !topicInRequestScope(req, topic))
30395
+ return jsonError(404, "Topic not found");
30396
+ const userId = requiredText(body.userId, "userId");
30397
+ const reason = body.reason === undefined ? undefined : requiredText(body.reason, "reason");
30398
+ const result = await topicService.reset({
30399
+ topicId,
30400
+ userId,
30401
+ reason: reason ?? "runtime-contract-session-reset"
30402
+ });
30403
+ if (result.isError)
30404
+ return jsonError(409, result.text);
30405
+ return Response.json({
30406
+ ok: true,
30407
+ v: NODE_RUNTIME_CONTRACT_VERSION,
30408
+ result: result.text
30409
+ });
30410
+ }
30411
+ const runtimeCompactMatch = runtimePath.match(/^\/topics\/([^/]+)\/session\/compact$/);
30412
+ if (runtimeCompactMatch && req.method === "POST") {
30413
+ const body = await bodyRecord(req);
30414
+ if (body.v !== NODE_RUNTIME_CONTRACT_VERSION)
30415
+ return jsonError(400, "Unsupported v");
30416
+ const topicId = decodeURIComponent(runtimeCompactMatch[1]);
30417
+ const topic = getTopic(topicId);
30418
+ if (!topic || !topicInRequestScope(req, topic))
30419
+ return jsonError(404, "Topic not found");
30420
+ const userId = requiredText(body.userId, "userId");
30421
+ const reason = body.reason === undefined ? undefined : requiredText(body.reason, "reason");
30422
+ const result = await topicService.compact({
30423
+ topicId,
30424
+ userId,
30425
+ reason: reason ?? "runtime-contract-session-compact",
30426
+ compactSession: options.compactSession
30427
+ });
30428
+ if (result.isError)
30429
+ return jsonError(409, result.text);
30430
+ return Response.json({
30431
+ ok: true,
30432
+ v: NODE_RUNTIME_CONTRACT_VERSION,
30433
+ result: result.text
30434
+ });
30435
+ }
30255
30436
  const runtimeTopicMatch = runtimePath.match(/^\/topics\/([^/]+)$/);
30256
30437
  if (runtimeTopicMatch && req.method === "GET") {
30257
30438
  const topic = getTopic(decodeURIComponent(runtimeTopicMatch[1]));
@@ -30261,6 +30442,69 @@ function createNodeControlHandler(options) {
30261
30442
  return jsonError(404, "Topic not found");
30262
30443
  return Response.json({ ok: true, v: NODE_RUNTIME_CONTRACT_VERSION, topic });
30263
30444
  }
30445
+ if (runtimeTopicMatch && req.method === "DELETE") {
30446
+ const topicId = decodeURIComponent(runtimeTopicMatch[1]);
30447
+ const topic = getTopic(topicId);
30448
+ if (!topic || !topicInRequestScope(req, topic))
30449
+ return jsonError(404, "Topic not found");
30450
+ const userId = requiredText(url.searchParams.get("user"), "user");
30451
+ try {
30452
+ await topicService.delete({ topicId, userId });
30453
+ } catch (err2) {
30454
+ if (err2 instanceof TopicServiceError) {
30455
+ const status = err2.code === "TOPIC_NOT_FOUND" ? 404 : err2.code === "TOPIC_FORBIDDEN" ? 403 : 400;
30456
+ return jsonError(status, err2.message);
30457
+ }
30458
+ throw err2;
30459
+ }
30460
+ return Response.json({ ok: true, v: NODE_RUNTIME_CONTRACT_VERSION });
30461
+ }
30462
+ if (runtimeTopicMatch && req.method === "PATCH") {
30463
+ const body = await bodyRecord(req);
30464
+ if (body.v !== NODE_RUNTIME_CONTRACT_VERSION)
30465
+ return jsonError(400, "Unsupported v");
30466
+ const topicId = decodeURIComponent(runtimeTopicMatch[1]);
30467
+ const existing = getTopic(topicId);
30468
+ if (!existing || !topicInRequestScope(req, existing)) {
30469
+ return jsonError(404, "Topic not found");
30470
+ }
30471
+ const userId = requiredText(body.userId, "userId");
30472
+ if (!existing.participants.some((participant) => participant.userId === userId)) {
30473
+ return jsonError(404, "Topic not found");
30474
+ }
30475
+ if (body.title !== undefined && typeof body.title !== "string") {
30476
+ return jsonError(400, "title must be a string");
30477
+ }
30478
+ if (body.agent !== undefined && body.agent !== null && !["claude", "codex", "maestro"].includes(String(body.agent))) {
30479
+ return jsonError(400, "Invalid agent");
30480
+ }
30481
+ if (body.defaultModel !== undefined && typeof body.defaultModel !== "string") {
30482
+ return jsonError(400, "defaultModel must be a string");
30483
+ }
30484
+ if (body.defaultEffort !== undefined && typeof body.defaultEffort !== "string") {
30485
+ return jsonError(400, "defaultEffort must be a string");
30486
+ }
30487
+ if (body.aiMode !== undefined && !["always", "mention", "off"].includes(String(body.aiMode))) {
30488
+ return jsonError(400, "Invalid aiMode");
30489
+ }
30490
+ try {
30491
+ const topic = updateTopicSettings({
30492
+ topicId,
30493
+ ...body.title !== undefined ? { title: body.title } : {},
30494
+ ...body.agent !== undefined ? { agent: body.agent ?? null } : {},
30495
+ ...body.defaultModel !== undefined ? { defaultModel: body.defaultModel } : {},
30496
+ ...body.defaultEffort !== undefined ? { defaultEffort: body.defaultEffort } : {},
30497
+ ...body.aiMode !== undefined ? { aiMode: body.aiMode } : {}
30498
+ });
30499
+ return Response.json({ ok: true, v: NODE_RUNTIME_CONTRACT_VERSION, topic });
30500
+ } catch (err2) {
30501
+ if (err2 instanceof TopicUpdateConflictError)
30502
+ return jsonError(409, err2.message);
30503
+ if (err2 instanceof TopicValidationError)
30504
+ return jsonError(400, err2.message);
30505
+ throw err2;
30506
+ }
30507
+ }
30264
30508
  return jsonError(404, "Runtime contract route not found");
30265
30509
  }
30266
30510
  if (req.method === "GET" && path === "/status") {
@@ -44989,8 +45233,13 @@ function allowedRuntimePath(path, method) {
44989
45233
  return true;
44990
45234
  return /^\/topics\/[^/]+(\/messages)?$/.test(path);
44991
45235
  }
44992
- if (method === "POST")
44993
- return path === "/turns";
45236
+ if (method === "POST") {
45237
+ if (path === "/turns")
45238
+ return true;
45239
+ return /^\/topics\/[^/]+\/(abort|session\/(reset|compact))$/.test(path);
45240
+ }
45241
+ if (method === "DELETE" || method === "PATCH")
45242
+ return /^\/topics\/[^/]+$/.test(path);
44994
45243
  return false;
44995
45244
  }
44996
45245
  async function forwardGatewayRequest(req, options) {
@@ -46932,4 +47181,4 @@ switch (command) {
46932
47181
  }
46933
47182
  }
46934
47183
 
46935
- //# debugId=40F9E6FE59E5730264756E2164756E21
47184
+ //# debugId=E9676B6176D9C6D664756E2164756E21