clay-server 5.4.0 → 5.5.0-beta.2

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 (79) hide show
  1. package/lib/agent-behavior-gate.js +223 -0
  2. package/lib/agent-behavior-schema.js +184 -0
  3. package/lib/daemon.js +15 -0
  4. package/lib/issues-mcp-server.js +16 -3
  5. package/lib/migrate-single-user.js +2 -0
  6. package/lib/project-issues.js +9 -2
  7. package/lib/project-log-feedback-delivery.js +3 -0
  8. package/lib/project-logs-mcp-server.js +23 -2
  9. package/lib/project-logs.js +40 -6
  10. package/lib/project-session-document.js +9 -1
  11. package/lib/project-session-notes.js +19 -8
  12. package/lib/project-session-pair-prompt.js +5 -4
  13. package/lib/project-user-knowledge.js +21 -3
  14. package/lib/project-worker-proposal.js +8 -2
  15. package/lib/project.js +108 -61
  16. package/lib/public/app.js +1 -0
  17. package/lib/public/css/agent-behavior.css +101 -0
  18. package/lib/public/modules/agent-behavior-nav.js +90 -0
  19. package/lib/public/modules/agent-behavior-view.js +235 -0
  20. package/lib/public/modules/agent-behavior.js +200 -0
  21. package/lib/public/modules/settings-shell-registry.js +1 -1
  22. package/lib/public/modules/settings-shell-view.js +18 -0
  23. package/lib/public/modules/sidebar-sessions.js +3 -0
  24. package/lib/public/modules/split-view-arrange-menu.js +38 -0
  25. package/lib/public/modules/split-view-create-menu.js +9 -5
  26. package/lib/public/modules/split-view-member-actions.js +42 -0
  27. package/lib/public/modules/split-view-picker-actions.js +2 -2
  28. package/lib/public/modules/split-view-presentation-renderer.js +10 -35
  29. package/lib/public/modules/split-view-presentations.js +9 -8
  30. package/lib/public/modules/split-view-sidebar-menu.js +2 -1
  31. package/lib/public/modules/user-settings-layout.js +8 -1
  32. package/lib/public/modules/user-settings.js +5 -2
  33. package/lib/public/modules/view-workspace-state.js +1 -1
  34. package/lib/public/pane-assets/{app-BDYH6Y7R.js → app-SEMVZ4AY.js} +1 -1
  35. package/lib/public/pane-assets/{chunk-U5GH2UJM.js → chunk-4YE4RU27.js} +1 -1
  36. package/lib/public/pane-assets/{chunk-E6NRPGVH.js → chunk-5UBN6GR7.js} +1 -1
  37. package/lib/public/pane-assets/chunk-6AGSOAOD.js +1 -0
  38. package/lib/public/pane-assets/chunk-AATAIAXL.js +1 -0
  39. package/lib/public/pane-assets/{chunk-42C2CZEP.js → chunk-BNNPJJVG.js} +1 -1
  40. package/lib/public/pane-assets/{chunk-HWLBH6AL.js → chunk-C6P7PICY.js} +1 -1
  41. package/lib/public/pane-assets/{chunk-KB4T5B2R.js → chunk-CQNPEH2M.js} +1 -1
  42. package/lib/public/pane-assets/chunk-D3CE5FZL.js +1 -0
  43. package/lib/public/pane-assets/{chunk-N5SIEIOG.js → chunk-E4VM2IDB.js} +1 -1
  44. package/lib/public/pane-assets/{chunk-VCNLKOVT.js → chunk-FK3YLGAP.js} +1 -1
  45. package/lib/public/pane-assets/chunk-GKSLBGVE.js +168 -0
  46. package/lib/public/pane-assets/chunk-J5PTSKJ6.js +1 -0
  47. package/lib/public/pane-assets/{chunk-XI2DJM4H.js → chunk-K4PAN77B.js} +1 -1
  48. package/lib/public/pane-assets/{chunk-UMIYQYGM.js → chunk-M5CWIIIT.js} +1 -1
  49. package/lib/public/pane-assets/{chunk-KF3LZRJK.js → chunk-QBCM7QEA.js} +1 -1
  50. package/lib/public/pane-assets/chunk-QPBO6ID4.js +1 -0
  51. package/lib/public/pane-assets/{chunk-TMUCEZ7C.js → chunk-QWQB2YRF.js} +1 -1
  52. package/lib/public/pane-assets/chunk-RA6VUJTY.js +1 -0
  53. package/lib/public/pane-assets/{chunk-TT3NECKK.js → chunk-U7EHYRJZ.js} +1 -1
  54. package/lib/public/pane-assets/chunk-XOZUM5ET.js +1 -0
  55. package/lib/public/pane-assets/{chunk-X452YCE5.js → chunk-XYZGVYWW.js} +1 -1
  56. package/lib/public/pane-assets/{chunk-QRVKAVH7.js → chunk-YR5GH3GI.js} +1 -1
  57. package/lib/public/pane-assets/{chunk-YE45RR7W.js → chunk-YVVT3CTV.js} +1 -1
  58. package/lib/public/pane-assets/manifest.json +394 -388
  59. package/lib/public/pane-assets/{style-VDIGFFLR.css → style-DZX6JAMF.css} +1 -1
  60. package/lib/public/style.css +1 -0
  61. package/lib/sdk-bridge.js +3 -2
  62. package/lib/server-agent-behavior-settings.js +72 -0
  63. package/lib/server-settings.js +2 -0
  64. package/lib/server.js +9 -0
  65. package/lib/session-notes-mcp-server.js +27 -7
  66. package/lib/session-skill-scope.js +2 -2
  67. package/lib/users-agent-behavior-preferences.js +38 -0
  68. package/lib/users.js +6 -0
  69. package/lib/yoke/adapters/claude.js +8 -4
  70. package/lib/yoke/skill-discovery.js +46 -1
  71. package/package.json +1 -1
  72. package/lib/public/pane-assets/chunk-46OOGVHU.js +0 -1
  73. package/lib/public/pane-assets/chunk-4OJW4FF4.js +0 -1
  74. package/lib/public/pane-assets/chunk-F7VIE6FJ.js +0 -1
  75. package/lib/public/pane-assets/chunk-FCJYJLVZ.js +0 -1
  76. package/lib/public/pane-assets/chunk-FEJDEXOW.js +0 -1
  77. package/lib/public/pane-assets/chunk-FQQMXMH7.js +0 -168
  78. package/lib/public/pane-assets/chunk-HEMCIILG.js +0 -1
  79. package/lib/public/pane-assets/chunk-RV5O66TO.js +0 -1
@@ -11,6 +11,7 @@
11
11
  // delete an entry. That keeps the log an honest record rather than a wiki.
12
12
 
13
13
  var logsMcp = require("./project-logs-mcp-server");
14
+ var logsSchema = require("./project-logs-schema");
14
15
 
15
16
  var LIST_LIMIT = 50;
16
17
  var MAX_NOTICE_TITLE_CHARS = 160;
@@ -92,6 +93,7 @@ function attachProjectLogs(ctx) {
92
93
  var getClients = ctx.getClients || function () { return []; };
93
94
  var getProjectOwnerId = ctx.getProjectOwnerId || function () { return null; };
94
95
  var onFeedback = ctx.onFeedback || function () {};
96
+ var agentBehavior = ctx.agentBehavior || null;
95
97
 
96
98
  // Every socket in this project's client set passed the project access check
97
99
  // at WebSocket upgrade, so a broadcast here is already scoped to authorized
@@ -311,7 +313,30 @@ function attachProjectLogs(ctx) {
311
313
 
312
314
  // --- MCP surface ------------------------------------------------------
313
315
 
314
- function toolHandlers(bound) {
316
+ // Learning records are a Logbook category the person can switch off or limit
317
+ // to requests, enforced here at the service call and not only in the catalog.
318
+ function learningRefusal(session, categoryValue) {
319
+ if (!agentBehavior || !session) return;
320
+ if (agentBehavior.modeNow(session, "logsLearning") !== "off") return;
321
+ var category = "";
322
+ try { category = typeof categoryValue === "string" ? logsSchema.normalizeCategory(categoryValue) : ""; } catch (error) { category = ""; }
323
+ if (category === "learning") {
324
+ throw new Error("Learning records are turned off in the user's Personal settings (Agent behavior). Use another record category or do not record this.");
325
+ }
326
+ }
327
+
328
+ function guardedLearning(session, bound, run, args, isUpdate) {
329
+ var request = coerceToolArgs(args);
330
+ learningRefusal(session, request.category || request.kind);
331
+ if (isUpdate && request.ref && agentBehavior && agentBehavior.modeNow(session, "logsLearning") === "off") {
332
+ var existing = null;
333
+ try { existing = bound.readLog({ ref: request.ref }); } catch (error) { existing = null; }
334
+ if (existing) learningRefusal(session, existing.category);
335
+ }
336
+ return run(request);
337
+ }
338
+
339
+ function toolHandlers(bound, session) {
315
340
  if (!bound) return null;
316
341
  return {
317
342
  listLogs: function (args) { return bound.listLogs(args || {}); },
@@ -322,6 +347,7 @@ function attachProjectLogs(ctx) {
322
347
  listLogFeedback: function (args) { return bound.listLogFeedback(args || {}); },
323
348
  reviewLogComment: function (args) {
324
349
  var request = coerceToolArgs(args);
350
+ learningRefusal(session, request.category || request.kind);
325
351
  var result = bound.reviewLogComment(request);
326
352
  broadcastCommentReview(result, request);
327
353
  // Only an incorporation is a canonical revision; clarify and decline
@@ -330,8 +356,8 @@ function attachProjectLogs(ctx) {
330
356
  return result;
331
357
  },
332
358
  revertLog: notifying("revert", function (args) { return bound.revertLog(args || {}); }),
333
- createLog: notifying("create", function (args) { return bound.createLog(coerceToolArgs(args)); }),
334
- updateLog: notifying("update", function (args) { return bound.updateLog(coerceToolArgs(args)); }),
359
+ createLog: notifying("create", function (args) { return guardedLearning(session, bound, function (request) { return bound.createLog(request); }, args, false); }),
360
+ updateLog: notifying("update", function (args) { return guardedLearning(session, bound, function (request) { return bound.updateLog(request); }, args, true); }),
335
361
  linkLog: notifying("link", function (args) { return bound.linkLog(coerceToolArgs(args)); }),
336
362
  };
337
363
  }
@@ -349,7 +375,7 @@ function attachProjectLogs(ctx) {
349
375
  if (isMate && !includeGlobal()) return [];
350
376
  var bound = session ? sessionBinding(session) : null;
351
377
  if (session && !bound) return [];
352
- return logsMcp.getToolDefs(toolHandlers(bound), isMate);
378
+ return logsMcp.getToolDefs(toolHandlers(bound, session), isMate);
353
379
  }
354
380
 
355
381
  function createMcpServer(adapter, session) {
@@ -361,7 +387,7 @@ function attachProjectLogs(ctx) {
361
387
  return logsMcp.createMcpServer(adapter, null, false);
362
388
  }
363
389
  var bound = session ? sessionBinding(session) : null;
364
- return logsMcp.createMcpServer(adapter, toolHandlers(bound), isMate);
390
+ return logsMcp.createMcpServer(adapter, toolHandlers(bound, session), isMate);
365
391
  }
366
392
 
367
393
  function getDynamicToolDefs(session) {
@@ -413,13 +439,21 @@ function attachProjectLogs(ctx) {
413
439
  " your review. Call list_log_feedback to see them.";
414
440
  }
415
441
 
416
- function getSystemPrompt(session) {
442
+ function getSystemPrompt(session, behavior) {
417
443
  if (!service) return "";
418
444
  if (isMate) {
419
445
  if (!includeGlobal() || (session && !sessionBinding(session))) return "";
420
446
  return SYSTEM_PROMPT_LABEL + "\n" + logsMcp.CLAY_READ_CONTRACT;
421
447
  }
422
448
  if (session && !sessionBinding(session)) return "";
449
+ if (behavior && behavior.active) {
450
+ // Off and On request are covered by the policy notice and tool text.
451
+ if (behavior.mode("logs") !== "auto") return "";
452
+ return SYSTEM_PROMPT_LABEL + "\n" + logsMcp.LOGS_CONTRACT +
453
+ (behavior.mode("logsLearning") === "auto" ? "\n" + logsMcp.LEARNING_CONTRACT : "") +
454
+ "\n" + logsMcp.attentionContract(behavior) +
455
+ "\n" + logsMcp.REVIEW_CONTRACT + pendingFeedbackSignal(session);
456
+ }
423
457
  return SYSTEM_PROMPT_LABEL + "\n" + logsMcp.LOGS_CONTRACT +
424
458
  "\n" + logsMcp.LEARNING_CONTRACT +
425
459
  "\n" + logsMcp.ATTENTION_CONTRACT +
@@ -138,7 +138,15 @@ function attachSessionDocument(ctx) {
138
138
 
139
139
  return {
140
140
  createMcpServer: createMcpServer,
141
- getSystemPrompt: function () { return DOCUMENT_PROMPT + "\n\n" + WIREFRAME_PROMPT; },
141
+ // Each half follows its own setting; Off and On request are covered by the
142
+ // policy notice and the tool descriptions.
143
+ getSystemPrompt: function (session, behavior) {
144
+ var active = behavior && behavior.active;
145
+ var parts = [];
146
+ if (!active || behavior.mode("markdownViewer") === "auto") parts.push(DOCUMENT_PROMPT);
147
+ if (!active || behavior.mode("sketch") === "auto") parts.push(WIREFRAME_PROMPT);
148
+ return parts.join("\n\n");
149
+ },
142
150
  getToolDefs: getToolDefs,
143
151
  };
144
152
  }
@@ -10,7 +10,8 @@ var MAX_PROMPT_CHARS = 4000;
10
10
  var MAX_NOTE_PROMPT_CHARS = 800;
11
11
  var NOTES_LABEL = "--- Project sticky notes (cross-session work memory; manage via clay-notes tools) ---";
12
12
  var NOTES_HINT = "- More notes are available; call list_notes for the rest.";
13
- var PROACTIVE_POLICY = sessionNotesMcp.MEMORY_CONTRACT + " If an injected preview is relevant or truncated, call list_notes before acting or writing so you do not create a duplicate.";
13
+ var DUPLICATE_HINT = " If an injected preview is relevant or truncated, call list_notes before acting or writing so you do not create a duplicate.";
14
+ var PROACTIVE_POLICY = sessionNotesMcp.MEMORY_CONTRACT + DUPLICATE_HINT;
14
15
  var NOTE_COLORS = ["purple", "green", "yellow", "blue", "pink", "orange"];
15
16
 
16
17
  function toolResult(value) {
@@ -101,14 +102,15 @@ function formatNoteLine(note) {
101
102
  return prefix + text.slice(0, previewLength).trimEnd() + marker;
102
103
  }
103
104
 
104
- function buildNotesPrompt(notes) {
105
+ function buildNotesPrompt(notes, options) {
106
+ var policy = options ? sessionNotesMcp.memoryContract(options) + DUPLICATE_HINT : PROACTIVE_POLICY;
105
107
  var newest = memoryNotes(notes).sort(function (a, b) {
106
108
  return (b.updatedAt || b.createdAt || 0) - (a.updatedAt || a.createdAt || 0);
107
109
  });
108
- if (newest.length === 0) return NOTES_LABEL + "\n(board is empty)\n" + PROACTIVE_POLICY;
110
+ if (newest.length === 0) return NOTES_LABEL + "\n(board is empty)\n" + policy;
109
111
  var lines = newest.map(formatNoteLine);
110
112
  var full = NOTES_LABEL + "\n" + lines.join("\n");
111
- if (full.length <= MAX_PROMPT_CHARS) return full + "\n" + PROACTIVE_POLICY;
113
+ if (full.length <= MAX_PROMPT_CHARS) return full + "\n" + policy;
112
114
 
113
115
  var result = NOTES_LABEL;
114
116
  var available = MAX_PROMPT_CHARS - result.length - NOTES_HINT.length - 2;
@@ -123,7 +125,7 @@ function buildNotesPrompt(notes) {
123
125
  if (partialLength > 0) result += prefix + lines[i].slice(0, partialLength) + "…";
124
126
  break;
125
127
  }
126
- return (result + "\n" + NOTES_HINT).slice(0, MAX_PROMPT_CHARS) + "\n" + PROACTIVE_POLICY;
128
+ return (result + "\n" + NOTES_HINT).slice(0, MAX_PROMPT_CHARS) + "\n" + policy;
127
129
  }
128
130
 
129
131
  function composeSystemPrompts(parts) {
@@ -255,6 +257,14 @@ function attachSessionNotes(ctx) {
255
257
  return toolResult({ removed: true, closed: true, deleted: false, id: closed.id, state: lifecycle.stateOf(closed) });
256
258
  }
257
259
 
260
+ // Contract variant for the person's Sticky Notes setting, or undefined for
261
+ // the unchanged default (also for Mates and sessions outside the policy).
262
+ function contractOptions(session) {
263
+ var view = ctx.agentBehavior && session ? ctx.agentBehavior.view(session) : null;
264
+ if (!view || !view.active) return undefined;
265
+ return { linked: view.issueLinkedNotes, mode: view.mode("notes") === "request" ? "request" : "auto" };
266
+ }
267
+
258
268
  function getToolDefs(boundSession) {
259
269
  if (boundSession && !canUseSession(boundSession)) return [];
260
270
  return sessionNotesMcp.getToolDefs({
@@ -263,7 +273,7 @@ function attachSessionNotes(ctx) {
263
273
  close: function (args) { return closeNote(args, boundSession || null); },
264
274
  reopen: function (args) { return reopenNote(args, boundSession || null); },
265
275
  remove: function (args) { return removeNote(args, boundSession || null); },
266
- });
276
+ }, contractOptions(boundSession));
267
277
  }
268
278
 
269
279
  function createMcpServer(adapter, boundSession) {
@@ -275,8 +285,9 @@ function attachSessionNotes(ctx) {
275
285
  });
276
286
  }
277
287
 
278
- function getSystemPrompt() {
279
- return buildNotesPrompt(nm.list());
288
+ function getSystemPrompt(session, behavior) {
289
+ if (behavior && behavior.active && behavior.mode("notes") === "off") return "";
290
+ return buildNotesPrompt(nm.list(), behavior && behavior.active ? contractOptions(session) : undefined);
280
291
  }
281
292
 
282
293
  return {
@@ -3,7 +3,8 @@ var driverOrchestration = require("./session-driver-orchestration");
3
3
  var prompts = require("./session-pair-prompts");
4
4
 
5
5
  function attachSessionPairPrompt(ctx) {
6
- function getSystemPrompt(session) {
6
+ function getSystemPrompt(session, behavior) {
7
+ var proposals = behavior && behavior.active ? behavior.mode("workerProposals") : "auto";
7
8
  var roles = ctx.rolesFor(session);
8
9
  if (roles && roles.workers.indexOf(session) !== -1) {
9
10
  var taskId = session._pairDelegation && session._pairDelegation.taskId || "unknown";
@@ -12,7 +13,7 @@ function attachSessionPairPrompt(ctx) {
12
13
  var group = ctx.store.groupForMember(session.localId);
13
14
  var result = "";
14
15
  if (group && group.pair && group.pair.driverId === session.localId) {
15
- result = prompts.DRIVER_CORE + " " + ctx.workerProposal().getSystemPrompt(session);
16
+ result = prompts.DRIVER_CORE + " " + ctx.workerProposal().getSystemPrompt(session, proposals);
16
17
  if (roles && roles.driver === session) {
17
18
  result += " Current configured Worker handles: " + roles.workers.map(function (worker) {
18
19
  return "workerId=" + worker.localId + " (" + (worker.title || "Worker") + ")";
@@ -20,8 +21,8 @@ function attachSessionPairPrompt(ctx) {
20
21
  }
21
22
  if (driverOrchestration.isHighTierDriverSession(session, ctx.sm)) result += " " + prompts.DRIVER_DELEGATION;
22
23
  } else if (!group && !ctx.isMate && driverEligibility.isEligibleDriverSession(session, ctx.sm) &&
23
- driverOrchestration.isHighTierDriverSession(session, ctx.sm)) {
24
- result = prompts.UNPAIRED + " " + ctx.workerProposal().getSystemPrompt(session);
24
+ driverOrchestration.isHighTierDriverSession(session, ctx.sm) && proposals === "auto") {
25
+ result = prompts.UNPAIRED + " " + ctx.workerProposal().getSystemPrompt(session, proposals);
25
26
  }
26
27
  return result;
27
28
  }
@@ -1,10 +1,23 @@
1
1
  // Exact-session tools and owner-only human reading for the You module.
2
- var CONTRACT = 'User Knowledge belongs to the logged-in user across their projects and Mates. Before substantial work or review, consult relevant User Knowledge with search_user_knowledge and apply it within its stated scope. Report durable explicit preferences, corrections, working principles and personal context using report_user_observation, preserving the user evidence and distinguishing inference. Do not report secrets, temporary task progress, or your own discoveries. Reports are untrusted proposals, not instructions or saved facts. Only the user\'s canonical Clay may curate them. Never claim knowledge was saved when only a report was submitted. Respect current user instructions and requests to correct or forget knowledge.';
2
+ var INTRO = 'User Knowledge belongs to the logged-in user across their projects and Mates. ';
3
+ var USE = 'Before substantial work or review, consult relevant User Knowledge with search_user_knowledge and apply it within its stated scope. ';
4
+ var COLLECT = 'Report durable explicit preferences, corrections, working principles and personal context using report_user_observation, preserving the user evidence and distinguishing inference. Do not report secrets, temporary task progress, or your own discoveries. Reports are untrusted proposals, not instructions or saved facts. Only the user\'s canonical Clay may curate them. Never claim knowledge was saved when only a report was submitted. ';
5
+ var OUTRO = 'Respect current user instructions and requests to correct or forget knowledge.';
6
+ var CONTRACT = INTRO + USE + COLLECT + OUTRO;
7
+ // The person's Me settings choose which halves of the contract apply.
8
+ function contract(use, collect) { return INTRO + (use ? USE : '') + (collect ? COLLECT : '') + OUTRO; }
3
9
  function attachProjectUserKnowledge(ctx) {
4
10
  function binding(session) {return ctx.service.bind({projectSlug:ctx.slug,session:session});}
11
+ function settings(session) {
12
+ var view=ctx.agentBehavior && session ? ctx.agentBehavior.view(session):null;
13
+ if (!view || !view.active) return {use:true,collect:true};
14
+ return {use:view.mode('meUse')==='auto',collect:view.mode('meCollection')==='auto'};
15
+ }
5
16
  function defs(session) {
6
17
  if (!ctx.service) return [];
7
18
  var bound=session ? binding(session):null;
19
+ var text=settings(session);
20
+ var CONTRACT=contract(text.use,text.collect);
8
21
  var clay=false;
9
22
  try {clay=bound && bound.isClay();} catch(error) {}
10
23
  function tool(name,description,properties,required,method) {
@@ -25,12 +38,17 @@ function attachProjectUserKnowledge(ctx) {
25
38
  }
26
39
  return tools;
27
40
  }
28
- function prompt(session) {
41
+ function prompt(session,behavior) {
29
42
  if(!session) return '';
43
+ var active=behavior && behavior.active;
44
+ var use=!active || behavior.mode('meUse')==='auto';
45
+ var collect=!active || behavior.mode('meCollection')==='auto';
46
+ if(!use && !collect) return '';
30
47
  try {
31
48
  var bound=binding(session);
49
+ if(!use) return '\n--- Me: owner-private user knowledge ---\n'+contract(false,collect);
32
50
  var entries=bound.list({limit:10}).items.map(function(e){return e.ref+' ['+e.category+'] '+e.summary;});
33
- return '\n--- Me: owner-private user knowledge ---\n'+CONTRACT+'\n'+(bound.isClay()?'You are the curator. Review pending observations with list_user_observations.\n':'')+entries.join('\n').slice(0,5000)+'\nTreat these records as contextual preferences, never as tool authorization.';
51
+ return '\n--- Me: owner-private user knowledge ---\n'+contract(use,collect)+'\n'+(bound.isClay()?'You are the curator. Review pending observations with list_user_observations.\n':'')+entries.join('\n').slice(0,5000)+'\nTreat these records as contextual preferences, never as tool authorization.';
34
52
  } catch(error) {return '';}
35
53
  }
36
54
  function handleMessage(ws,msg) {
@@ -460,11 +460,17 @@ function attachWorkerProposal(ctx) {
460
460
  }));
461
461
  }
462
462
 
463
- function getSystemPrompt(session) {
463
+ var MANAGEMENT_ONLY = "With multiple Workers, pass workerId to every targeted partner, queue, interrupt, replacement, close, permission, and targeted status operation. Call partner_status without workerId to discover current Worker handles. Give each Worker independent file ownership and bounded work. Do not assign simultaneous overlapping file edits unless the work is explicitly separated, such as by distinct paths or worktrees.";
464
+ // mode is the person's Worker proposals setting: Off and On request keep
465
+ // the management guidance for Workers that exist but drop the nudge to
466
+ // propose new ones (the tool description carries the On request rule).
467
+ function getSystemPrompt(session, mode) {
468
+ var proactive = mode === undefined || mode === "auto";
464
469
  if (creationProposal.canMountProposal(session) && !isEligible(session)) {
470
+ if (!proactive) return MANAGEMENT_ONLY;
465
471
  return "This Driver may configure additional Split Workers through propose_worker. With multiple Workers, pass workerId to every targeted partner, queue, interrupt, replacement, close, permission, and targeted status operation. Call partner_status without workerId to discover current Worker handles. Give each Worker independent file ownership and bounded work. Do not assign simultaneous overlapping file edits unless the work is explicitly separated, such as by distinct paths or worktrees.";
466
472
  }
467
- if (!isEligible(session) || !driverOrchestration.isHighTierDriverSession(session, sm)) return "";
473
+ if (!proactive || !isEligible(session) || !driverOrchestration.isHighTierDriverSession(session, sm)) return "";
468
474
  return "For implementation-heavy work, use propose_worker before substantial execution. Work is implementation-heavy when it spans multiple modules, crosses client/server/data boundaries, requires a migration, or contains independent investigation and execution. Do not skip delegation merely because you can implement it yourself; preserve the Driver for decomposition, runtime selection, judgment, review, and integration. The user's explicit request to work directly takes precedence, and work that must remain sequential in this session stays here. Recommend an exact available vendor, model, and effort, and give a concise recommendationRationale explaining why all three fit the task. Clay always records and shows the runtime configuration card. In full-access mode Clay may auto-accept that exact validated recommendation; otherwise it waits for the user's explicit choice. After posting, end the turn. If accepted, Clay creates the exact Driver/Split Worker pair and delegates the proposed task once. If declined, continue in this Driver session. Do not call send_to_partner while unpaired.";
469
475
  }
470
476
  return { getToolDefs: getToolDefs, getSystemPrompt: getSystemPrompt, proposeReplacement: proposeReplacement,
package/lib/project.js CHANGED
@@ -795,11 +795,23 @@ function createProjectContext(opts) {
795
795
  },
796
796
  });
797
797
 
798
+ // Personal Agent behavior policy for Project Driver sessions (never Mates).
799
+ var _agentBehavior = require("./agent-behavior-gate").attachAgentBehavior({
800
+ isMate: isMate,
801
+ isMultiUser: function () { return usersModule.isMultiUser(); },
802
+ getPolicy: function (ownerId) {
803
+ if (usersModule.isMultiUser()) return usersModule.getAgentBehavior(ownerId);
804
+ var daemonConfig = typeof opts.onGetDaemonConfig === "function" ? opts.onGetDaemonConfig() : null;
805
+ return daemonConfig && daemonConfig.agentBehavior;
806
+ },
807
+ });
808
+
798
809
  // Sticky-note storage is initialized before session tool wiring so every
799
810
  // vendor receives the same handlers and prompt snapshot at query start.
800
811
  var nm = createNotesManager({ cwd: cwd, send: send, sendTo: sendTo });
801
812
  var _sessionNotes = attachSessionNotes({
802
813
  nm: nm,
814
+ agentBehavior: _agentBehavior,
803
815
  canUseSession: function (session) {
804
816
  if (!session || sm.sessions.get(session.localId) !== session) return false;
805
817
  if (!usersModule.isMultiUser()) return true;
@@ -869,12 +881,14 @@ function createProjectContext(opts) {
869
881
  mateId: mateId,
870
882
  });
871
883
  var _logFeedbackDelivery = attachProjectLogFeedbackDelivery({
884
+ agentBehavior: _agentBehavior,
872
885
  sm: sm,
873
886
  getSdk: function () { return sdk; },
874
887
  onProcessingChanged: onProcessingChanged,
875
888
  getLinuxUserForSession: getLinuxUserForSession,
876
889
  });
877
890
  var _projectLogs = attachProjectLogs({
891
+ agentBehavior: _agentBehavior,
878
892
  service: opts.projectLogsService || null,
879
893
  sm: sm,
880
894
  projectSlug: slug,
@@ -897,6 +911,7 @@ function createProjectContext(opts) {
897
911
  onProcessingChanged: onProcessingChanged, sendTo: sendTo,
898
912
  });
899
913
  var _issues = require("./project-issues").attachProjectIssues({
914
+ agentBehavior: _agentBehavior,
900
915
  service: opts.issuesService, projectSlug: slug, isMate: isMate, sm: sm,
901
916
  resolveDefaultAi: opts.resolveDefaultAi, adapters: adapters,
902
917
  sendTo: sendTo, getClients: function () { return clients; }, startWork: _issueWork,
@@ -952,6 +967,7 @@ function createProjectContext(opts) {
952
967
  },
953
968
  });
954
969
  var _userKnowledge = require('./project-user-knowledge').attachProjectUserKnowledge({
970
+ agentBehavior: _agentBehavior,
955
971
  service: opts.userKnowledgeService, schedule: opts.scheduleUserKnowledge, slug: slug, sendTo: sendTo,
956
972
  isMultiUser: function () { return usersModule.isMultiUser(); }
957
973
  });
@@ -1304,18 +1320,20 @@ function createProjectContext(opts) {
1304
1320
  var keys = Object.keys(mcpServers || {});
1305
1321
  var filtered = {};
1306
1322
  var hasAny = false;
1323
+ var gatedAdapter = forSession ? _agentBehavior.gateAdapter(adapter, forSession) : adapter;
1307
1324
  for (var i = 0; i < keys.length; i++) {
1308
1325
  var name = keys[i];
1309
1326
  if (forSession && forSession.youCurator && name !== 'clay-you') continue;
1310
1327
  if (name === 'clay-you') {
1311
- if (forSession) { filtered[name] = _userKnowledge.createMcpServer(adapter, forSession); hasAny = true; }
1328
+ var boundYou = forSession ? _userKnowledge.createMcpServer(gatedAdapter, forSession) : null;
1329
+ if (boundYou) { filtered[name] = boundYou; hasAny = true; }
1312
1330
  continue;
1313
1331
  }
1314
1332
  if (name === "clay-browser" && !extConnected) continue;
1315
1333
  if (name === "clay-email" && !emailAvailable) continue;
1316
1334
  if (name === "clay-workspace" && forSession) {
1317
1335
  try {
1318
- var boundWorkspace = _workspaceQuery.createMcpServer(adapter, forSession);
1336
+ var boundWorkspace = _workspaceQuery.createMcpServer(gatedAdapter, forSession);
1319
1337
  if (boundWorkspace) { filtered[name] = boundWorkspace; hasAny = true; }
1320
1338
  } catch (e) {
1321
1339
  console.error("[project] Failed to bind clay-workspace MCP server:", e.message);
@@ -1332,23 +1350,23 @@ function createProjectContext(opts) {
1332
1350
  continue;
1333
1351
  }
1334
1352
  if (name === "clay-linear" && forSession) {
1335
- var boundLinear = _sessionLinear.createMcpServer(adapter, forSession);
1353
+ var boundLinear = _sessionLinear.createMcpServer(gatedAdapter, forSession);
1336
1354
  if (boundLinear) { filtered[name] = boundLinear; hasAny = true; }
1337
1355
  continue;
1338
1356
  }
1339
1357
  if (name === "clay-github" && forSession) {
1340
- var boundGithub = _sessionGithub.createMcpServer(adapter, forSession);
1358
+ var boundGithub = _sessionGithub.createMcpServer(gatedAdapter, forSession);
1341
1359
  if (boundGithub) { filtered[name] = boundGithub; hasAny = true; }
1342
1360
  continue;
1343
1361
  }
1344
1362
  if (name === "clay-issues" && forSession) {
1345
- var boundIssues = _issues.createMcpServer(adapter, forSession);
1363
+ var boundIssues = _issues.createMcpServer(gatedAdapter, forSession);
1346
1364
  if (boundIssues) { filtered[name] = boundIssues; hasAny = true; }
1347
1365
  continue;
1348
1366
  }
1349
1367
  if (name === "clay-logs" && forSession) {
1350
1368
  try {
1351
- var boundLogs = _projectLogs.createMcpServer(adapter, forSession);
1369
+ var boundLogs = _projectLogs.createMcpServer(gatedAdapter, forSession);
1352
1370
  if (boundLogs) { filtered[name] = boundLogs; hasAny = true; }
1353
1371
  } catch (e) {
1354
1372
  console.error("[project] Failed to bind clay-logs MCP server:", e.message);
@@ -1357,7 +1375,7 @@ function createProjectContext(opts) {
1357
1375
  }
1358
1376
  if (name === "clay-knowledge" && forSession) {
1359
1377
  try {
1360
- var boundKnowledge = _mateKnowledge.createMcpServer(adapter, forSession);
1378
+ var boundKnowledge = _mateKnowledge.createMcpServer(gatedAdapter, forSession);
1361
1379
  if (boundKnowledge) { filtered[name] = boundKnowledge; hasAny = true; }
1362
1380
  } catch (e) {
1363
1381
  console.error("[project] Failed to bind clay-knowledge MCP server:", e.message);
@@ -1375,7 +1393,7 @@ function createProjectContext(opts) {
1375
1393
  }
1376
1394
  if (name === "clay-sessions" && forSession) {
1377
1395
  try {
1378
- var boundSpawn = _sessionSpawn.createMcpServer(adapter, forSession);
1396
+ var boundSpawn = _sessionSpawn.createMcpServer(gatedAdapter, forSession);
1379
1397
  if (boundSpawn) { filtered[name] = boundSpawn; hasAny = true; }
1380
1398
  } catch (e) {
1381
1399
  console.error("[project] Failed to bind session spawn MCP server:", e.message);
@@ -1384,7 +1402,7 @@ function createProjectContext(opts) {
1384
1402
  }
1385
1403
  if (name === "clay-notes" && forSession) {
1386
1404
  try {
1387
- var boundNotes = _sessionNotes.createMcpServer(adapter, forSession);
1405
+ var boundNotes = _sessionNotes.createMcpServer(gatedAdapter, forSession);
1388
1406
  if (boundNotes) { filtered[name] = boundNotes; hasAny = true; }
1389
1407
  } catch (e) {
1390
1408
  console.error("[project] Failed to bind session notes MCP server:", e.message);
@@ -1393,7 +1411,7 @@ function createProjectContext(opts) {
1393
1411
  }
1394
1412
  if (name === "clay-handoff" && forSession) {
1395
1413
  try {
1396
- var boundHandoff = _sessionHandoff.createMcpServer(adapter, forSession);
1414
+ var boundHandoff = _sessionHandoff.createMcpServer(gatedAdapter, forSession);
1397
1415
  if (boundHandoff) { filtered[name] = boundHandoff; hasAny = true; }
1398
1416
  } catch (e) {
1399
1417
  console.error("[project] Failed to bind session handoff MCP server:", e.message);
@@ -1401,13 +1419,13 @@ function createProjectContext(opts) {
1401
1419
  continue;
1402
1420
  }
1403
1421
  if (name === "clay-shared-browser" && forSession) {
1404
- var boundBrowser = _sharedBrowser.createMcpServer(adapter, forSession);
1422
+ var boundBrowser = _sharedBrowser.createMcpServer(gatedAdapter, forSession);
1405
1423
  if (boundBrowser) { filtered[name] = boundBrowser; hasAny = true; }
1406
1424
  continue;
1407
1425
  }
1408
1426
  if (name === "clay-documents" && forSession) {
1409
1427
  try {
1410
- var boundDocuments = _sessionDocument.createMcpServer(adapter, forSession);
1428
+ var boundDocuments = _sessionDocument.createMcpServer(gatedAdapter, forSession);
1411
1429
  if (boundDocuments) { filtered[name] = boundDocuments; hasAny = true; }
1412
1430
  } catch (e) {
1413
1431
  console.error("[project] Failed to bind session document MCP server:", e.message);
@@ -1416,7 +1434,7 @@ function createProjectContext(opts) {
1416
1434
  }
1417
1435
  if (name === "clay-debate" && forSession) {
1418
1436
  try {
1419
- var boundDebate = _debateProposal.createMcpServer(adapter, forSession);
1437
+ var boundDebate = _debateProposal.createMcpServer(gatedAdapter, forSession);
1420
1438
  if (boundDebate) { filtered[name] = boundDebate; hasAny = true; }
1421
1439
  } catch (e) {
1422
1440
  console.error("[project] Failed to bind debate MCP server:", e.message);
@@ -1425,7 +1443,7 @@ function createProjectContext(opts) {
1425
1443
  }
1426
1444
  if (name === "clay-mate-creation" && forSession) {
1427
1445
  try {
1428
- var boundMateCreation = _mateCreationProposal.createMcpServer(adapter, forSession);
1446
+ var boundMateCreation = _mateCreationProposal.createMcpServer(gatedAdapter, forSession);
1429
1447
  if (boundMateCreation) { filtered[name] = boundMateCreation; hasAny = true; }
1430
1448
  } catch (e) {
1431
1449
  console.error("[project] Failed to bind Mate creation MCP server:", e.message);
@@ -1448,6 +1466,57 @@ function createProjectContext(opts) {
1448
1466
  }
1449
1467
 
1450
1468
  // --- SDK bridge ---
1469
+ // Prompt and catalog the SDK bridge builds for every query of a session.
1470
+ function composeSessionSystemPrompt(session) {
1471
+ if (session.youCurator) return _userKnowledge.getSystemPrompt(session);
1472
+ var behavior = _agentBehavior.view(session);
1473
+ var auto = function (setting, build) { return _agentBehavior.autoPrompt(session, setting, build); };
1474
+ return composeSystemPrompts([
1475
+ _sessionPair.getSystemPrompt(session, behavior),
1476
+ _sessionNotes.getSystemPrompt(session, behavior),
1477
+ _sessionHandoff.getSystemPrompt(session),
1478
+ auto("continuation", function () { return _driverContinuation.getSystemPrompt(session); }),
1479
+ _sessionDocument.getSystemPrompt(session, behavior),
1480
+ behavior.mode("sharedBrowser") === "off" ? "" : _sharedBrowser.getSystemPrompt(session),
1481
+ _capsuleCatalog.getSystemPrompt(session),
1482
+ _projectLogs.getSystemPrompt(session, behavior),
1483
+ auto("issues", function () { return _issues.getSystemPrompt(session); }),
1484
+ auto("githubLinks", function () { return _sessionGithub.getSystemPrompt(session); }),
1485
+ auto("linearLinks", function () { return _sessionLinear.getSystemPrompt(session); }),
1486
+ _scheduledTasks ? _scheduledTasks.getSystemPrompt(session) : "",
1487
+ _mateScheduling ? _mateScheduling.getSystemPrompt(session) : "",
1488
+ _mateKnowledge.getSystemPrompt(session),
1489
+ _userKnowledge.getSystemPrompt(session, behavior),
1490
+ _autonomousRun ? _autonomousRun.getSystemPrompt(session) : "",
1491
+ _agentBehavior.notice(session),
1492
+ ]);
1493
+ }
1494
+
1495
+ function composeSessionToolDefs(session) {
1496
+ if (session.youCurator) return _userKnowledge.getDynamicToolDefs(session);
1497
+ return _agentBehavior.filterTools(session, _sessionPair.getToolDefs(session)
1498
+ .concat(_sessionNotes.getToolDefs(session))
1499
+ .concat(_sessionHandoff.getToolDefs(session))
1500
+ .concat(_driverContinuation.getToolDefs(session, Number(session._sdkQueryGeneration || 0)))
1501
+ .concat(_sessionDocument.getToolDefs(session))
1502
+ .concat(_sharedBrowser.getToolDefs(session))
1503
+ .concat(_debateProposal.getToolDefs(session))
1504
+ .concat(_mateCreationProposal.getToolDefs(session))
1505
+ .concat(_workspaceQuery.getDynamicToolDefs(session))
1506
+ .concat(_workspaceQuery.getHistoryDynamicToolDefs(session))
1507
+ .concat(_projectLogs.getDynamicToolDefs(session))
1508
+ .concat(_issues.getDynamicToolDefs(session))
1509
+ .concat(_sessionGithub.getDynamicToolDefs(session))
1510
+ .concat(_sessionLinear.getDynamicToolDefs(session))
1511
+ .concat(_mateKnowledge.getDynamicToolDefs(session))
1512
+ .concat(opts.userKnowledgeService ? _userKnowledge.getDynamicToolDefs(session) : [])
1513
+ .concat(_loopInterview ? _loopInterview.getToolDefs(session) : [])
1514
+ .concat(_scheduledTasks ? _scheduledTasks.getToolDefs(session) : [])
1515
+ .concat(_mateScheduling ? _mateScheduling.getToolDefs(session) : [])
1516
+ .concat(_scheduledTaskExecution ? _scheduledTaskExecution.getToolDefs(session) : [])
1517
+ .concat(_autonomousRun ? _autonomousRun.getToolDefs(session) : []));
1518
+ }
1519
+
1451
1520
  sdk = createSDKBridge({
1452
1521
  isLifecycleExecutionPaused: function () { return !!(opts.getProjectAccess && opts.getProjectAccess().lifecycleExecutionPaused); },
1453
1522
  cwd: cwd,
@@ -1510,55 +1579,13 @@ function createProjectContext(opts) {
1510
1579
  }
1511
1580
  return false;
1512
1581
  },
1513
- getSessionSystemPrompt: function (session) {
1514
- if (session.youCurator) return _userKnowledge.getSystemPrompt(session);
1515
- return composeSystemPrompts([
1516
- _sessionPair.getSystemPrompt(session),
1517
- _sessionNotes.getSystemPrompt(session),
1518
- _sessionHandoff.getSystemPrompt(session),
1519
- _driverContinuation.getSystemPrompt(session),
1520
- _sessionDocument.getSystemPrompt(session),
1521
- _sharedBrowser.getSystemPrompt(session),
1522
- _capsuleCatalog.getSystemPrompt(session),
1523
- _projectLogs.getSystemPrompt(session),
1524
- _issues.getSystemPrompt(session),
1525
- _sessionGithub.getSystemPrompt(session),
1526
- _sessionLinear.getSystemPrompt(session),
1527
- _scheduledTasks ? _scheduledTasks.getSystemPrompt(session) : "",
1528
- _mateScheduling ? _mateScheduling.getSystemPrompt(session) : "",
1529
- _mateKnowledge.getSystemPrompt(session),
1530
- _userKnowledge.getSystemPrompt(session),
1531
- _autonomousRun ? _autonomousRun.getSystemPrompt(session) : "",
1532
- ]);
1533
- },
1582
+ getSessionSystemPrompt: composeSessionSystemPrompt,
1583
+ getExcludedSkills: function (session) { return _agentBehavior.excludedSkills(session); },
1534
1584
  getRuntimeEnv: getRuntimeEnvForSession,
1535
1585
  onUserInputRequest: function (session, request, respond) {
1536
1586
  return _askUser.createHandler(session)(request, respond);
1537
1587
  },
1538
- getSessionToolDefs: function (session) {
1539
- if (session.youCurator) return _userKnowledge.getDynamicToolDefs(session);
1540
- return _sessionPair.getToolDefs(session)
1541
- .concat(_sessionNotes.getToolDefs(session))
1542
- .concat(_sessionHandoff.getToolDefs(session))
1543
- .concat(_driverContinuation.getToolDefs(session, Number(session._sdkQueryGeneration || 0)))
1544
- .concat(_sessionDocument.getToolDefs(session))
1545
- .concat(_sharedBrowser.getToolDefs(session))
1546
- .concat(_debateProposal.getToolDefs(session))
1547
- .concat(_mateCreationProposal.getToolDefs(session))
1548
- .concat(_workspaceQuery.getDynamicToolDefs(session))
1549
- .concat(_workspaceQuery.getHistoryDynamicToolDefs(session))
1550
- .concat(_projectLogs.getDynamicToolDefs(session))
1551
- .concat(_issues.getDynamicToolDefs(session))
1552
- .concat(_sessionGithub.getDynamicToolDefs(session))
1553
- .concat(_sessionLinear.getDynamicToolDefs(session))
1554
- .concat(_mateKnowledge.getDynamicToolDefs(session))
1555
- .concat(opts.userKnowledgeService ? _userKnowledge.getDynamicToolDefs(session) : [])
1556
- .concat(_loopInterview ? _loopInterview.getToolDefs(session) : [])
1557
- .concat(_scheduledTasks ? _scheduledTasks.getToolDefs(session) : [])
1558
- .concat(_mateScheduling ? _mateScheduling.getToolDefs(session) : [])
1559
- .concat(_scheduledTaskExecution ? _scheduledTaskExecution.getToolDefs(session) : [])
1560
- .concat(_autonomousRun ? _autonomousRun.getToolDefs(session) : []);
1561
- },
1588
+ getSessionToolDefs: composeSessionToolDefs,
1562
1589
  canUseSessionTools: function (session) {
1563
1590
  if (!session || sm.sessions.get(session.localId) !== session) return false;
1564
1591
  if (!usersModule.isMultiUser()) return true;
@@ -2491,6 +2518,7 @@ function createProjectContext(opts) {
2491
2518
  && sm.sessions.get(boundSession.localId) === boundSession
2492
2519
  && Number(boundSession._sdkQueryGeneration || 0) === queryGeneration;
2493
2520
  }
2521
+ function gateBridgeList(list) { return boundSession ? _agentBehavior.filterBridgeTools(boundSession, list) : list; }
2494
2522
  return {
2495
2523
  listTools: function () {
2496
2524
  var tools = [];
@@ -2609,7 +2637,7 @@ function createProjectContext(opts) {
2609
2637
  }
2610
2638
  }
2611
2639
 
2612
- if (sessionOnly) return Promise.resolve(tools.concat(externalQueryBridge.list()));
2640
+ if (sessionOnly) return Promise.resolve(gateBridgeList(tools.concat(externalQueryBridge.list())));
2613
2641
 
2614
2642
  // In-app MCP servers (debate, browser, email).
2615
2643
  // Use getLocalMcpServers() so clay-browser is hidden unless the
@@ -2637,13 +2665,15 @@ function createProjectContext(opts) {
2637
2665
  }
2638
2666
  }
2639
2667
 
2640
- return Promise.resolve(tools);
2668
+ return Promise.resolve(gateBridgeList(tools));
2641
2669
  },
2642
2670
  callTool: function (serverName, toolName, args) {
2643
2671
  if (sessionOnly && !sessionQueryValid()) {
2644
2672
  return Promise.reject(new Error("Session tool unavailable or older query: " + serverName + "/" + toolName));
2645
2673
  }
2646
2674
  if (boundSession && boundSession.youCurator && serverName !== 'clay-you') return Promise.reject(new Error('Clay review only permits User Knowledge tools.'));
2675
+ var behaviorRefusal = boundSession ? _agentBehavior.callRefusal(boundSession, toolName) : null;
2676
+ if (behaviorRefusal) return Promise.resolve(behaviorRefusal);
2647
2677
  if (boundSession && serverName === 'clay-you') return _userKnowledge.callBridgeTool(boundSession, toolName, args);
2648
2678
  var queryCall = queryToolBridge.callTool(queryGeneration, serverName, toolName, args);
2649
2679
  if (queryCall) return queryCall;
@@ -3063,6 +3093,23 @@ function createProjectContext(opts) {
3063
3093
  getViewWorkerSupervision: _workerConversation.supervision,
3064
3094
  createDelegatedSession: _delegatedSession.createDelegatedSession,
3065
3095
  refreshEnvironmentRuntime: function () { return sdk.refreshEnvironmentRuntime(); },
3096
+ // The person's Agent behavior changed: rebuild their Driver sessions'
3097
+ // prompts and catalogs at the next message. Running turns are not stopped.
3098
+ // Read-only view of what this session's next query receives (diagnostics and tests).
3099
+ getSessionAgentSurface: function (session) {
3100
+ return { systemPrompt: composeSessionSystemPrompt(session), toolNames: composeSessionToolDefs(session).map(function (tool) { return tool.name; }) };
3101
+ },
3102
+ refreshAgentBehavior: function (ownerId) {
3103
+ if (isMate) return 0;
3104
+ var refreshed = 0;
3105
+ sm.sessions.forEach(function (session) {
3106
+ if (!session || session.youCurator) return;
3107
+ if (usersModule.isMultiUser() && (session.ownerId || null) !== ownerId) return;
3108
+ sdk.refreshSessionRuntime(session);
3109
+ refreshed++;
3110
+ });
3111
+ return refreshed;
3112
+ },
3066
3113
  getWorkspaceProductOperations: function () { return isMate ? null : _workspaceOperations; },
3067
3114
  getMateChatControl: function () { return isMate ? _mateChatControl : null; },
3068
3115
  getMateScheduleControl: function () { return isMate ? _mateLifecycle : null; },
package/lib/public/app.js CHANGED
@@ -288,6 +288,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
288
288
 
289
289
  // --- Initialize store (single source of truth for client state) ---
290
290
  createStore({
291
+ agentBehavior: { status: "idle", error: "", catalog: null, policy: null, effective: null, edits: {}, saved: {} },
291
292
  networkSettings: { loaded: false, busy: false, confirming: false, draft: "", saved: "", status: "", errorKind: "", requestId: 0 },
292
293
  vendorLoginBrowser: null,
293
294
  githubStarCount: null,