@questionbase/deskfree 0.3.0-alpha.20 → 0.3.0-alpha.22

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/index.js CHANGED
@@ -3857,19 +3857,12 @@ var DeskFreeClient = class {
3857
3857
  return this.request("POST", "messages.update", input);
3858
3858
  }
3859
3859
  /**
3860
- * Send a text message (with optional attachments or suggestions) to a DeskFree conversation.
3860
+ * Send a text message to a DeskFree conversation.
3861
3861
  *
3862
- * @param input - Message content, optional userId, taskId, attachments, and suggestions
3862
+ * @param input - Message content, optional userId, taskId, attachments
3863
3863
  */
3864
3864
  async sendMessage(input) {
3865
- if (!input.content && !input.suggestions) {
3866
- throw new DeskFreeError(
3867
- "client",
3868
- "content",
3869
- "content or suggestions is required",
3870
- "Missing required parameter: provide content or suggestions."
3871
- );
3872
- }
3865
+ this.requireNonEmpty(input.content, "content");
3873
3866
  return this.request("POST", "messages.send", input);
3874
3867
  }
3875
3868
  /** Fetch paginated message history for a conversation. */
@@ -3889,21 +3882,30 @@ var DeskFreeClient = class {
3889
3882
  return this.request("POST", "messages.wsTicket", {});
3890
3883
  }
3891
3884
  // ── Tasks ─────────────────────────────────────────────────
3892
- /** Create a new task, optionally with a recurring schedule. */
3893
- async createTask(input) {
3894
- this.requireNonEmpty(input.title, "title");
3895
- return this.request("POST", "tasks.create", input);
3896
- }
3897
3885
  /** Claim a task so the bot can begin working on it. Returns enriched context. */
3898
3886
  async claimTask(input) {
3899
3887
  this.requireNonEmpty(input.taskId, "taskId");
3900
3888
  return this.request("POST", "tasks.claim", input);
3901
3889
  }
3902
- /** Update the deliverable (markdown or HTML content) for a task. */
3903
- async updateDeliverable(input) {
3890
+ /** Fetch a lightweight task summary by ID. Read-only, no side effects. */
3891
+ async getTask(input) {
3904
3892
  this.requireNonEmpty(input.taskId, "taskId");
3905
- this.requireNonEmpty(input.deliverable, "deliverable");
3906
- return this.request("POST", "tasks.updateDeliverable", input);
3893
+ return this.request("GET", "tasks.get", input);
3894
+ }
3895
+ /** Update the content of an existing file. */
3896
+ async updateFile(input) {
3897
+ this.requireNonEmpty(input.fileId, "fileId");
3898
+ this.requireNonEmpty(input.content, "content");
3899
+ return this.request("POST", "files.update", input);
3900
+ }
3901
+ /** Create a new persistent file. */
3902
+ async createFile(input) {
3903
+ this.requireNonEmpty(input.name, "name");
3904
+ return this.request("POST", "files.create", input);
3905
+ }
3906
+ /** List all files for this bot (metadata only, no content). */
3907
+ async listFiles() {
3908
+ return this.request("GET", "files.list", {});
3907
3909
  }
3908
3910
  /** Send an agent status update to DeskFree. */
3909
3911
  async statusUpdate(input) {
@@ -3928,20 +3930,17 @@ var DeskFreeClient = class {
3928
3930
  this.requireNonEmpty(input.taskId, "taskId");
3929
3931
  return this.request("POST", "tasks.complete", input);
3930
3932
  }
3931
- /** Suggest new tasks for the human to review and approve (via messages.send with suggestions). */
3932
- async suggestTasks(input) {
3933
- if (!input.tasks || input.tasks.length === 0) {
3933
+ /** Suggest tasks via the dedicated bot/tasks.suggest endpoint. */
3934
+ async suggestTasksDedicated(input) {
3935
+ if (!input.suggestions || input.suggestions.length === 0) {
3934
3936
  throw new DeskFreeError(
3935
3937
  "client",
3936
- "tasks",
3937
- "tasks array is required and cannot be empty",
3938
- "Missing required parameter: tasks. Please provide at least one task to suggest."
3938
+ "suggestions",
3939
+ "suggestions array is required and cannot be empty",
3940
+ "Missing required parameter: suggestions."
3939
3941
  );
3940
3942
  }
3941
- return this.sendMessage({
3942
- suggestions: input.tasks,
3943
- taskId: input.taskId
3944
- });
3943
+ return this.request("POST", "tasks.suggest", input);
3945
3944
  }
3946
3945
  /**
3947
3946
  * Claim a pending evaluation for a task. Atomically sets isWorking=true where
@@ -3953,7 +3952,7 @@ var DeskFreeClient = class {
3953
3952
  }
3954
3953
  /**
3955
3954
  * Submit the result of a ways-of-working evaluation.
3956
- * If hasChanges is true and updatedContent is provided, inserts a new version.
3955
+ * Dual output: globalWoW (applies everywhere) and initiative (applies to the task's initiative).
3957
3956
  * Clears evaluationPending and isWorking on the task.
3958
3957
  */
3959
3958
  async submitEvaluation(input) {
@@ -4422,6 +4421,20 @@ async function fetchAndSaveMedia(attachment) {
4422
4421
  }
4423
4422
  }
4424
4423
  }
4424
+ var MAX_INSTRUCTIONS_LENGTH = 500;
4425
+ function truncateAtWord(text, maxLen) {
4426
+ if (text.length <= maxLen) return text;
4427
+ const truncated = text.slice(0, maxLen);
4428
+ const lastSpace = truncated.lastIndexOf(" ");
4429
+ return (lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated) + "\u2026";
4430
+ }
4431
+ function buildBodyForAgent(task, content) {
4432
+ let prefix = `[Task thread: "${task.title}" | status: ${task.status}]`;
4433
+ if (task.instructions) {
4434
+ prefix += "\n" + truncateAtWord(task.instructions, MAX_INSTRUCTIONS_LENGTH);
4435
+ }
4436
+ return prefix + "\n\n" + content;
4437
+ }
4425
4438
  async function deliverMessageToAgent(ctx, message, client) {
4426
4439
  const runtime = getDeskFreeRuntime();
4427
4440
  const log = ctx.log ?? runtime.logging.createLogger("deskfree:deliver");
@@ -4464,9 +4477,20 @@ async function deliverMessageToAgent(ctx, message, client) {
4464
4477
  );
4465
4478
  }
4466
4479
  }
4480
+ let bodyForAgent;
4481
+ if (message.taskId) {
4482
+ try {
4483
+ const task = await client.getTask({ taskId: message.taskId });
4484
+ bodyForAgent = buildBodyForAgent(task, message.content);
4485
+ } catch (err) {
4486
+ const errMsg = err instanceof Error ? err.message : String(err);
4487
+ log.warn(`Failed to fetch task context for ${message.taskId}: ${errMsg}`);
4488
+ }
4489
+ }
4467
4490
  const msgCtx = runtime.channel.reply.finalizeInboundContext({
4468
4491
  Body: message.content,
4469
4492
  RawBody: message.content,
4493
+ ...bodyForAgent ? { BodyForAgent: bodyForAgent } : {},
4470
4494
  ChatType: "dm",
4471
4495
  Provider: "deskfree",
4472
4496
  Surface: "deskfree",
@@ -4578,6 +4602,7 @@ var wrapper_default = import_websocket.default;
4578
4602
  // src/gateway.ts
4579
4603
  var activeTaskId = null;
4580
4604
  var completedTaskId = null;
4605
+ var inboundThreadId = null;
4581
4606
  function setActiveTaskId(taskId) {
4582
4607
  if (taskId === null && activeTaskId !== null) {
4583
4608
  completedTaskId = activeTaskId;
@@ -4585,15 +4610,19 @@ function setActiveTaskId(taskId) {
4585
4610
  activeTaskId = taskId;
4586
4611
  }
4587
4612
  function getActiveTaskId() {
4588
- return activeTaskId ?? completedTaskId;
4613
+ return activeTaskId ?? completedTaskId ?? inboundThreadId;
4589
4614
  }
4590
4615
  function clearCompletedTaskId() {
4591
4616
  completedTaskId = null;
4592
4617
  }
4618
+ function setInboundThreadId(taskId) {
4619
+ inboundThreadId = taskId;
4620
+ }
4593
4621
  var PING_INTERVAL_MS = 5 * 60 * 1e3;
4594
4622
  var POLL_FALLBACK_INTERVAL_MS = 30 * 1e3;
4595
4623
  var WS_CONNECTION_TIMEOUT_MS = 30 * 1e3;
4596
4624
  var WS_PONG_TIMEOUT_MS = 10 * 1e3;
4625
+ var NOTIFY_DEBOUNCE_MS = 200;
4597
4626
  var BACKOFF_INITIAL_MS = 2e3;
4598
4627
  var BACKOFF_MAX_MS = 3e4;
4599
4628
  var BACKOFF_FACTOR = 1.8;
@@ -4817,6 +4846,7 @@ async function runWebSocketConnection(opts) {
4817
4846
  let pingInterval;
4818
4847
  let connectionTimer;
4819
4848
  let pongTimer;
4849
+ let notifyDebounceTimer;
4820
4850
  let isConnected = false;
4821
4851
  const cleanup = () => {
4822
4852
  if (pingInterval !== void 0) {
@@ -4831,6 +4861,10 @@ async function runWebSocketConnection(opts) {
4831
4861
  clearTimeout(pongTimer);
4832
4862
  pongTimer = void 0;
4833
4863
  }
4864
+ if (notifyDebounceTimer !== void 0) {
4865
+ clearTimeout(notifyDebounceTimer);
4866
+ notifyDebounceTimer = void 0;
4867
+ }
4834
4868
  };
4835
4869
  connectionTimer = setTimeout(() => {
4836
4870
  if (!isConnected) {
@@ -4911,15 +4945,21 @@ async function runWebSocketConnection(opts) {
4911
4945
  return;
4912
4946
  }
4913
4947
  if (msg.action === "notify") {
4914
- enqueuePoll(
4915
- client,
4916
- ctx,
4917
- () => cursor,
4918
- (c) => {
4919
- cursor = c ?? cursor;
4920
- },
4921
- log
4922
- );
4948
+ if (notifyDebounceTimer !== void 0) {
4949
+ clearTimeout(notifyDebounceTimer);
4950
+ }
4951
+ notifyDebounceTimer = setTimeout(() => {
4952
+ notifyDebounceTimer = void 0;
4953
+ enqueuePoll(
4954
+ client,
4955
+ ctx,
4956
+ () => cursor,
4957
+ (c) => {
4958
+ cursor = c ?? cursor;
4959
+ },
4960
+ log
4961
+ );
4962
+ }, NOTIFY_DEBOUNCE_MS);
4923
4963
  } else if (msg.action === "pong") {
4924
4964
  if (pongTimer !== void 0) {
4925
4965
  clearTimeout(pongTimer);
@@ -5116,6 +5156,7 @@ async function pollAndDeliver(client, ctx, cursor, log, account) {
5116
5156
  continue;
5117
5157
  }
5118
5158
  clearCompletedTaskId();
5159
+ setInboundThreadId(message.taskId ?? null);
5119
5160
  await deliverMessageToAgent(ctx, message, client);
5120
5161
  deliveredMessageIds.add(message.messageId);
5121
5162
  deliveredCount++;
@@ -7755,79 +7796,160 @@ var Type = type_exports2;
7755
7796
  var ORCHESTRATOR_TOOLS = {
7756
7797
  STATE: {
7757
7798
  name: "deskfree_state",
7758
- description: "Get full workspace state \u2014 all tasks, recently done tasks. Use to assess what needs attention.",
7799
+ description: "Get full workspace state \u2014 all tasks, recently done tasks, active initiatives. Use to assess what needs attention.",
7759
7800
  parameters: Type.Object({})
7760
7801
  },
7761
- CREATE_TASK: {
7762
- name: "deskfree_create_task",
7763
- description: "Create a new task (starts as bot).",
7764
- parameters: Type.Object({
7765
- title: Type.String({ description: "Task title (max 200 chars)" }),
7766
- instructions: Type.Optional(
7767
- Type.String({ description: "Detailed instructions for the task" })
7768
- )
7769
- })
7770
- },
7771
7802
  START_TASK: {
7772
7803
  name: "deskfree_start_task",
7773
- description: "Claim a bot task (isWorking=false) and start working. Returns full context (instructions, deliverable, message history).",
7804
+ description: "Claim a bot task (isWorking=false) and start working. Returns full context (instructions, message history, and fileContext if the task has a linked file \u2014 use the file content as working context).",
7774
7805
  parameters: Type.Object({
7775
7806
  taskId: Type.String({ description: "Task UUID to claim" })
7776
7807
  })
7777
7808
  },
7778
- UPDATE_DELIVERABLE: {
7779
- name: "deskfree_update_deliverable",
7780
- description: 'Update task deliverable. Build incrementally as you work. Use format="html" when delivering rich web content (reports, dashboards, interactive pages); use format="markdown" (default) for everything else.',
7809
+ UPDATE_FILE: {
7810
+ name: "deskfree_update_file",
7811
+ description: `Update a file's content. Use this to save work to a persistent file linked to your task. Call incrementally as you build content. Use format="html" for rich web content; use format="markdown" (default) for everything else.`,
7781
7812
  parameters: Type.Object({
7782
- taskId: Type.String({ description: "Task UUID" }),
7783
- deliverable: Type.String({
7784
- description: "Deliverable content (markdown or HTML depending on format)"
7813
+ fileId: Type.String({ description: "File ID to update" }),
7814
+ content: Type.String({
7815
+ description: "Full file content (replaces previous)"
7785
7816
  }),
7786
- format: Type.Optional(
7817
+ contentFormat: Type.Optional(
7787
7818
  Type.Union([Type.Literal("markdown"), Type.Literal("html")], {
7788
- description: '"markdown" (default) for text/documents, "html" for rich web content. HTML is rendered in a sandboxed iframe.'
7819
+ description: '"markdown" (default) for text/documents, "html" for rich web content.'
7820
+ })
7821
+ )
7822
+ })
7823
+ },
7824
+ CREATE_FILE: {
7825
+ name: "deskfree_create_file",
7826
+ description: "Create a new persistent file. Use when your task produces a document that should persist beyond the task lifecycle (reports, specs, runbooks, etc.).",
7827
+ parameters: Type.Object({
7828
+ name: Type.String({ description: "File name (max 200 chars)" }),
7829
+ description: Type.Optional(
7830
+ Type.String({
7831
+ description: "Brief description of the file's purpose (max 2000 chars)"
7832
+ })
7833
+ ),
7834
+ content: Type.Optional(
7835
+ Type.String({ description: "Initial file content" })
7836
+ ),
7837
+ contentFormat: Type.Optional(
7838
+ Type.Union([Type.Literal("markdown"), Type.Literal("html")], {
7839
+ description: '"markdown" (default) or "html"'
7789
7840
  })
7790
7841
  )
7791
7842
  })
7792
7843
  },
7793
7844
  COMPLETE_TASK: {
7794
7845
  name: "deskfree_complete_task",
7795
- description: 'Finish a task. Outcome "done" = work complete for review. Outcome "blocked" = need human input. Both move to human.',
7846
+ description: 'Finish a task. Outcome "done" = work complete (summary required). Outcome "blocked" = need human input. Both move to human.',
7796
7847
  parameters: Type.Object({
7797
7848
  taskId: Type.String({ description: "Task UUID" }),
7798
7849
  outcome: Type.Union([Type.Literal("done"), Type.Literal("blocked")], {
7799
7850
  description: '"done" = work complete, "blocked" = need human input'
7800
- })
7851
+ }),
7852
+ summary: Type.Optional(
7853
+ Type.String({
7854
+ description: 'Brief summary of what was accomplished (required for outcome "done", max 2000 chars)'
7855
+ })
7856
+ )
7801
7857
  })
7802
7858
  },
7803
7859
  SEND_MESSAGE: {
7804
7860
  name: "deskfree_send_message",
7805
- description: "Send a message in the task thread (progress update, question, status report). Can also suggest follow-up tasks for human review by providing the suggestions parameter instead of content.",
7861
+ description: "Send a message in the task thread (progress update, question, status report).",
7806
7862
  parameters: Type.Object({
7807
- content: Type.Optional(
7863
+ content: Type.String({
7864
+ description: "Message content."
7865
+ }),
7866
+ taskId: Type.Optional(
7808
7867
  Type.String({
7809
- description: "Message content. Required unless suggestions is provided."
7868
+ description: "Task UUID (optional if context provides it)"
7810
7869
  })
7870
+ )
7871
+ })
7872
+ },
7873
+ SUGGEST_TASKS: {
7874
+ name: "deskfree_suggest_tasks",
7875
+ description: 'Suggest tasks for human approval. Tasks are created with "suggested" status \u2014 the human approves or rejects each one. Use this to propose work before starting. Include detailed instructions as if briefing a contractor.',
7876
+ parameters: Type.Object({
7877
+ suggestions: Type.Array(
7878
+ Type.Object({
7879
+ title: Type.String({
7880
+ description: "Task title \u2014 short, action-oriented (max 200 chars)"
7881
+ }),
7882
+ instructions: Type.Optional(
7883
+ Type.String({
7884
+ description: "Detailed instructions: what to do, why, what done looks like, known constraints"
7885
+ })
7886
+ ),
7887
+ estimatedTokens: Type.Optional(
7888
+ Type.Number({
7889
+ description: "Estimated token cost \u2014 consider files to read, reasoning, output"
7890
+ })
7891
+ ),
7892
+ initiativeId: Type.Optional(
7893
+ Type.String({
7894
+ description: "Link to existing initiative ID \u2014 set when this task belongs to an active initiative"
7895
+ })
7896
+ ),
7897
+ scheduledFor: Type.Optional(
7898
+ Type.String({
7899
+ description: "ISO-8601 date for when this task should become available. Use for future-dated or recurring work."
7900
+ })
7901
+ ),
7902
+ fileId: Type.Optional(
7903
+ Type.String({
7904
+ description: "Link to an existing file ID \u2014 the bot will receive the file content when claiming this task"
7905
+ })
7906
+ ),
7907
+ newFile: Type.Optional(
7908
+ Type.Object(
7909
+ {
7910
+ name: Type.String({ description: "File name (max 200 chars)" }),
7911
+ description: Type.Optional(
7912
+ Type.String({
7913
+ description: "Brief description of the file's purpose"
7914
+ })
7915
+ )
7916
+ },
7917
+ {
7918
+ description: "Create a new file and link it to this task \u2014 use when the task will produce a persistent document"
7919
+ }
7920
+ )
7921
+ )
7922
+ }),
7923
+ {
7924
+ description: "Array of task suggestions (1-20)",
7925
+ minItems: 1,
7926
+ maxItems: 20
7927
+ }
7811
7928
  ),
7812
- taskId: Type.Optional(
7929
+ parentTaskId: Type.Optional(
7813
7930
  Type.String({
7814
- description: "Task UUID (optional if context provides it)"
7931
+ description: "Parent task ID \u2014 set when suggesting follow-ups from within a task"
7815
7932
  })
7816
7933
  ),
7817
- suggestions: Type.Optional(
7934
+ initiativeSuggestions: Type.Optional(
7818
7935
  Type.Array(
7819
7936
  Type.Object({
7820
- title: Type.String({ description: "Task title (max 200 chars)" }),
7821
- instructions: Type.Optional(
7822
- Type.String({
7823
- description: "Detailed instructions for the suggested task"
7937
+ title: Type.String({
7938
+ description: "Initiative title (max 200 chars)"
7939
+ }),
7940
+ content: Type.String({
7941
+ description: "Initiative content markdown \u2014 current state, approach, and next priorities"
7942
+ }),
7943
+ taskRefs: Type.Optional(
7944
+ Type.Array(Type.Number(), {
7945
+ description: "Indexes into suggestions[] to auto-link when this initiative is approved"
7824
7946
  })
7825
7947
  )
7826
7948
  }),
7827
7949
  {
7828
- description: "Suggest tasks for human review (1-10). The human will see approve/reject buttons for each. Provide this instead of content.",
7950
+ description: 'Propose new initiatives (optional). Created with "suggested" status \u2014 human approves or rejects independently.',
7829
7951
  minItems: 1,
7830
- maxItems: 10
7952
+ maxItems: 5
7831
7953
  }
7832
7954
  )
7833
7955
  )
@@ -7835,34 +7957,59 @@ var ORCHESTRATOR_TOOLS = {
7835
7957
  },
7836
7958
  CLAIM_EVALUATION: {
7837
7959
  name: "deskfree_claim_evaluation",
7838
- description: "Claim a pending ways-of-working evaluation for a task. Returns the task, its message history, and current ways_of_working content so you can decide whether updates are needed. Returns null if already claimed by another process.",
7960
+ description: "Claim a pending ways-of-working evaluation for a task. Returns the task, its message history, current global ways_of_working, and initiative data (if the task belongs to an initiative). Returns null if already claimed by another process.",
7839
7961
  parameters: Type.Object({
7840
7962
  taskId: Type.String({ description: "Task UUID to claim evaluation for" })
7841
7963
  })
7842
7964
  },
7843
7965
  SUBMIT_EVALUATION: {
7844
7966
  name: "deskfree_submit_evaluation",
7845
- description: "Submit the result of a ways-of-working evaluation. Provide reasoning explaining your analysis. If the task revealed new patterns or learnings worth capturing, set hasChanges=true and provide updatedContent with the full updated markdown. Otherwise set hasChanges=false.",
7967
+ description: "Submit the result of a ways-of-working evaluation. Provide reasoning explaining your analysis. Evaluation has two independent outputs: globalWoW (applies everywhere) and initiative (applies to the specific initiative this task belongs to). Set hasChanges=true and provide updatedContent for each output you want to update.",
7846
7968
  parameters: Type.Object({
7847
7969
  taskId: Type.String({ description: "Task UUID being evaluated" }),
7848
7970
  reasoning: Type.String({
7849
- description: "Explanation of your evaluation \u2014 what you analyzed and why you did or did not update the ways of working"
7850
- }),
7851
- hasChanges: Type.Boolean({
7852
- description: "Whether the ways of working should be updated"
7971
+ description: "Explanation of your evaluation \u2014 what you analyzed and why you did or did not update the ways of working and/or initiative content"
7853
7972
  }),
7854
- updatedContent: Type.Optional(
7855
- Type.String({
7856
- description: "Full updated ways-of-working markdown (required if hasChanges=true)"
7857
- })
7973
+ globalWoW: Type.Object(
7974
+ {
7975
+ hasChanges: Type.Boolean({
7976
+ description: "Whether the global ways of working should be updated"
7977
+ }),
7978
+ updatedContent: Type.Optional(
7979
+ Type.String({
7980
+ description: "Full updated global ways-of-working markdown (required if hasChanges=true)"
7981
+ })
7982
+ )
7983
+ },
7984
+ {
7985
+ description: "Global ways-of-working update \u2014 patterns that apply across all work"
7986
+ }
7987
+ ),
7988
+ initiative: Type.Object(
7989
+ {
7990
+ hasChanges: Type.Boolean({
7991
+ description: "Whether the initiative content should be updated (ignored if task has no initiative)"
7992
+ }),
7993
+ updatedContent: Type.Optional(
7994
+ Type.String({
7995
+ description: "Full updated initiative content markdown (required if hasChanges=true)"
7996
+ })
7997
+ )
7998
+ },
7999
+ {
8000
+ description: "Initiative content update \u2014 what was learned about this specific area of focus. Ignored if the task has no initiative_id."
8001
+ }
7858
8002
  )
7859
8003
  })
7860
8004
  }
7861
8005
  };
7862
8006
  var WORKER_TOOLS = {
7863
- UPDATE_DELIVERABLE: ORCHESTRATOR_TOOLS.UPDATE_DELIVERABLE,
8007
+ UPDATE_FILE: ORCHESTRATOR_TOOLS.UPDATE_FILE,
8008
+ CREATE_FILE: ORCHESTRATOR_TOOLS.CREATE_FILE,
7864
8009
  COMPLETE_TASK: ORCHESTRATOR_TOOLS.COMPLETE_TASK,
7865
8010
  SEND_MESSAGE: ORCHESTRATOR_TOOLS.SEND_MESSAGE,
8011
+ SUGGEST_TASKS: ORCHESTRATOR_TOOLS.SUGGEST_TASKS,
8012
+ CLAIM_EVALUATION: ORCHESTRATOR_TOOLS.CLAIM_EVALUATION,
7866
8013
  SUBMIT_EVALUATION: ORCHESTRATOR_TOOLS.SUBMIT_EVALUATION
7867
8014
  };
7868
8015
  var CHANNEL_META = {
@@ -8420,14 +8567,22 @@ var deskFreePlugin = {
8420
8567
  };
8421
8568
 
8422
8569
  // src/context.ts
8423
- var DESKFREE_AGENT_DIRECTIVE = `## DeskFree
8424
- Always read the deskfree skill (SKILL.md) at startup. Follow its workflow for task tracking and messaging.
8425
- 1. ALL work requires a task \u2014 use \`deskfree_create_task\` then \`deskfree_start_task\`.
8426
- 2. Ways of Working is your evolving playbook \u2014 it's injected automatically via state.get. When tasks are approved by humans, you'll evaluate them and update the ways of working.
8427
- 3. Build deliverables incrementally \u2014 use \`deskfree_update_deliverable\` from the start.
8428
- 4. Always complete tasks \u2014 use \`deskfree_complete_task\` with outcome "done" or "blocked".
8429
- 5. Auto-threading works \u2014 messages sent while a task is active are threaded automatically.
8430
- 6. Sub-agents get 4 tools: update_deliverable, complete_task, send_message (also supports suggestions), submit_evaluation.`;
8570
+ var DESKFREE_AGENT_DIRECTIVE = `## DeskFree \u2014 The Work Loop
8571
+ Always read the deskfree skill (SKILL.md) at startup. Follow the suggest-first work loop:
8572
+ 1. **Check state** \u2192 \`deskfree_state\` \u2014 see what needs attention, read ways of working, check files.
8573
+ 2. **Suggest tasks** \u2192 \`deskfree_suggest_tasks\` \u2014 ALL work requires human-approved tasks first. Use \`newFile\` to pre-create a file for tasks that will produce persistent documents.
8574
+ 3. **Claim a task** \u2192 \`deskfree_start_task\` \u2014 read instructions + parent context. If task has a linked file, its content is in \`fileContext\`.
8575
+ 4. **Do the work** \u2192 use \`deskfree_update_file\` to save work to linked files incrementally. Create new files with \`deskfree_create_file\` when needed.
8576
+ 5. **Suggest follow-ups** \u2192 if work reveals more to do, suggest them (strongest when you have full context).
8577
+ 6. **Complete** \u2192 \`deskfree_complete_task\` \u2014 summary required for outcome "done".
8578
+ 7. **Evaluate** \u2192 check \`pendingEvaluations\` and update ways of working.
8579
+
8580
+ Key principles:
8581
+ - You're building a chain. Your instructions become someone else's brief. Your file output becomes someone else's context.
8582
+ - Write instructions as if briefing a contractor who has never seen the codebase.
8583
+ - MUST provide summary when completing with outcome "done".
8584
+ - Estimate token cost per suggestion \u2014 consider files to read, reasoning, output.
8585
+ - Sub-agents get 7 tools: update_file, create_file, complete_task, send_message, suggest_tasks, claim_evaluation, submit_evaluation.`;
8431
8586
  function getDeskFreeContext() {
8432
8587
  return `${DESKFREE_AGENT_DIRECTIVE}
8433
8588
 
@@ -8466,15 +8621,7 @@ function formatTaskResponse(task, summary, nextActions) {
8466
8621
  content: [
8467
8622
  {
8468
8623
  type: "text",
8469
- text: JSON.stringify(
8470
- {
8471
- summary,
8472
- nextActions,
8473
- task
8474
- },
8475
- null,
8476
- 2
8477
- )
8624
+ text: JSON.stringify({ summary, nextActions, task }, null, 2)
8478
8625
  }
8479
8626
  ]
8480
8627
  };
@@ -8485,11 +8632,7 @@ function formatConfirmation(summary, nextActions, data) {
8485
8632
  {
8486
8633
  type: "text",
8487
8634
  text: JSON.stringify(
8488
- {
8489
- summary,
8490
- nextActions,
8491
- ...data ? { data } : {}
8492
- },
8635
+ { summary, nextActions, ...data ? { data } : {} },
8493
8636
  null,
8494
8637
  2
8495
8638
  )
@@ -8550,14 +8693,7 @@ function errorResult(err) {
8550
8693
  content: [
8551
8694
  {
8552
8695
  type: "text",
8553
- text: JSON.stringify(
8554
- {
8555
- error: true,
8556
- message: rawMessage
8557
- },
8558
- null,
8559
- 2
8560
- )
8696
+ text: JSON.stringify({ error: true, message: rawMessage }, null, 2)
8561
8697
  }
8562
8698
  ],
8563
8699
  isError: true
@@ -8574,9 +8710,7 @@ function validateStringParam(params, key, required) {
8574
8710
  }
8575
8711
  const value = params[key];
8576
8712
  if (value == null) {
8577
- if (required) {
8578
- throw new Error(`Required parameter '${key}' is missing`);
8579
- }
8713
+ if (required) throw new Error(`Required parameter '${key}' is missing`);
8580
8714
  return void 0;
8581
8715
  }
8582
8716
  if (typeof value !== "string") {
@@ -8598,9 +8732,7 @@ function validateEnumParam(params, key, allowedValues, required) {
8598
8732
  }
8599
8733
  const value = params[key];
8600
8734
  if (value == null) {
8601
- if (required) {
8602
- throw new Error(`Required parameter '${key}' is missing`);
8603
- }
8735
+ if (required) throw new Error(`Required parameter '${key}' is missing`);
8604
8736
  return void 0;
8605
8737
  }
8606
8738
  if (typeof value !== "string") {
@@ -8616,6 +8748,298 @@ function validateEnumParam(params, key, allowedValues, required) {
8616
8748
  }
8617
8749
  return value;
8618
8750
  }
8751
+ function parseSuggestions(raw) {
8752
+ if (!Array.isArray(raw) || raw.length === 0) {
8753
+ throw new Error('Parameter "suggestions" must be a non-empty array');
8754
+ }
8755
+ return raw.map((s, i) => {
8756
+ if (typeof s !== "object" || s === null) {
8757
+ throw new Error(`suggestions[${i}] must be an object`);
8758
+ }
8759
+ const item = s;
8760
+ const title = item["title"];
8761
+ if (typeof title !== "string" || title.trim() === "") {
8762
+ throw new Error(`suggestions[${i}].title must be a non-empty string`);
8763
+ }
8764
+ let newFile;
8765
+ if (item["newFile"] && typeof item["newFile"] === "object") {
8766
+ const nf = item["newFile"];
8767
+ if (typeof nf["name"] === "string" && nf["name"].trim()) {
8768
+ newFile = {
8769
+ name: nf["name"].trim(),
8770
+ description: typeof nf["description"] === "string" ? nf["description"] : void 0
8771
+ };
8772
+ }
8773
+ }
8774
+ return {
8775
+ title: title.trim(),
8776
+ instructions: typeof item["instructions"] === "string" ? item["instructions"] : void 0,
8777
+ estimatedTokens: typeof item["estimatedTokens"] === "number" ? item["estimatedTokens"] : void 0,
8778
+ initiativeId: typeof item["initiativeId"] === "string" ? item["initiativeId"] : void 0,
8779
+ scheduledFor: typeof item["scheduledFor"] === "string" ? item["scheduledFor"] : void 0,
8780
+ fileId: typeof item["fileId"] === "string" ? item["fileId"] : void 0,
8781
+ newFile
8782
+ };
8783
+ });
8784
+ }
8785
+ function parseInitiativeSuggestions(raw) {
8786
+ if (!Array.isArray(raw) || raw.length === 0) return void 0;
8787
+ return raw.map((s, i) => {
8788
+ if (typeof s !== "object" || s === null) {
8789
+ throw new Error(`initiativeSuggestions[${i}] must be an object`);
8790
+ }
8791
+ const item = s;
8792
+ const title = item["title"];
8793
+ if (typeof title !== "string" || title.trim() === "") {
8794
+ throw new Error(
8795
+ `initiativeSuggestions[${i}].title must be a non-empty string`
8796
+ );
8797
+ }
8798
+ const content = item["content"];
8799
+ if (typeof content !== "string") {
8800
+ throw new Error(`initiativeSuggestions[${i}].content must be a string`);
8801
+ }
8802
+ return {
8803
+ title: title.trim(),
8804
+ content,
8805
+ taskRefs: Array.isArray(item["taskRefs"]) ? item["taskRefs"] : void 0
8806
+ };
8807
+ });
8808
+ }
8809
+ function parseEvaluationSection(raw, name) {
8810
+ if (typeof raw !== "object" || raw === null) {
8811
+ throw new Error(`Parameter "${name}" must be an object`);
8812
+ }
8813
+ const obj = raw;
8814
+ if (typeof obj["hasChanges"] !== "boolean") {
8815
+ throw new Error(`Parameter "${name}.hasChanges" must be a boolean`);
8816
+ }
8817
+ return {
8818
+ hasChanges: obj["hasChanges"],
8819
+ updatedContent: typeof obj["updatedContent"] === "string" ? obj["updatedContent"] : void 0
8820
+ };
8821
+ }
8822
+ function makeUpdateFileHandler(client) {
8823
+ return async (_id, params) => {
8824
+ try {
8825
+ const fileId = validateStringParam(params, "fileId", true);
8826
+ const content = validateStringParam(params, "content", true);
8827
+ const contentFormat = validateEnumParam(
8828
+ params,
8829
+ "contentFormat",
8830
+ ["markdown", "html"],
8831
+ false
8832
+ );
8833
+ await client.updateFile({ fileId, content, contentFormat });
8834
+ return formatConfirmation(`Updated file ${fileId}`, [
8835
+ "File content has been saved",
8836
+ "Complete the task with deskfree_complete_task when ready"
8837
+ ]);
8838
+ } catch (err) {
8839
+ return errorResult(err);
8840
+ }
8841
+ };
8842
+ }
8843
+ function makeCreateFileHandler(client) {
8844
+ return async (_id, params) => {
8845
+ try {
8846
+ const name = validateStringParam(params, "name", true);
8847
+ const description = validateStringParam(params, "description", false);
8848
+ const content = validateStringParam(params, "content", false);
8849
+ const contentFormat = validateEnumParam(
8850
+ params,
8851
+ "contentFormat",
8852
+ ["markdown", "html"],
8853
+ false
8854
+ );
8855
+ const result = await client.createFile({
8856
+ name,
8857
+ description,
8858
+ content,
8859
+ contentFormat
8860
+ });
8861
+ return formatConfirmation(
8862
+ `Created file "${name}"`,
8863
+ [
8864
+ `File ID: ${result.fileId}`,
8865
+ "Use deskfree_update_file to update its content"
8866
+ ],
8867
+ result
8868
+ );
8869
+ } catch (err) {
8870
+ return errorResult(err);
8871
+ }
8872
+ };
8873
+ }
8874
+ function makeCompleteTaskHandler(client) {
8875
+ return async (_id, params) => {
8876
+ try {
8877
+ const taskId = validateStringParam(params, "taskId", true);
8878
+ const outcome = validateEnumParam(
8879
+ params,
8880
+ "outcome",
8881
+ ["done", "blocked"],
8882
+ true
8883
+ );
8884
+ const summary = validateStringParam(params, "summary", false);
8885
+ const result = await client.completeTask({ taskId, outcome, summary });
8886
+ setActiveTaskId(null);
8887
+ const summaryVerb = outcome === "done" ? "completed" : "blocked";
8888
+ const icon = outcome === "done" ? "\u2705" : "\u{1F6AB}";
8889
+ await client.sendMessage({
8890
+ content: `${icon} Task ${summaryVerb}: "${result.title}"`
8891
+ }).catch(() => {
8892
+ });
8893
+ return formatTaskResponse(
8894
+ result,
8895
+ `Task "${result.title}" marked as ${summaryVerb} \u2014 waiting for human`,
8896
+ [
8897
+ outcome === "done" ? "Human will review your work summary" : "Human will review the blocker and provide guidance",
8898
+ "Use deskfree_state to check for other tasks"
8899
+ ]
8900
+ );
8901
+ } catch (err) {
8902
+ return errorResult(err);
8903
+ }
8904
+ };
8905
+ }
8906
+ function makeSendMessageHandler(client) {
8907
+ return async (_id, params) => {
8908
+ try {
8909
+ const content = validateStringParam(params, "content", true);
8910
+ const taskId = validateStringParam(params, "taskId", false);
8911
+ await client.sendMessage({ content, taskId });
8912
+ return formatConfirmation(
8913
+ `Message sent${taskId ? ` to task ${taskId}` : ""}`,
8914
+ [
8915
+ "Message delivered to the human",
8916
+ taskId ? "Continue working on the task or wait for response" : "Check for response with task messages"
8917
+ ]
8918
+ );
8919
+ } catch (err) {
8920
+ return errorResult(err);
8921
+ }
8922
+ };
8923
+ }
8924
+ function makeSuggestTasksHandler(client) {
8925
+ return async (_id, params) => {
8926
+ try {
8927
+ const suggestions = parseSuggestions(params?.suggestions);
8928
+ const parentTaskId = validateStringParam(params, "parentTaskId", false);
8929
+ const initiativeSuggestions = parseInitiativeSuggestions(
8930
+ params?.initiativeSuggestions
8931
+ );
8932
+ const result = await client.suggestTasksDedicated({
8933
+ suggestions,
8934
+ parentTaskId,
8935
+ initiativeSuggestions
8936
+ });
8937
+ const initiativeCount = initiativeSuggestions?.length ?? 0;
8938
+ const summaryParts = [
8939
+ `Suggested ${suggestions.length} task${suggestions.length === 1 ? "" : "s"} for human approval`,
8940
+ ...initiativeCount > 0 ? [
8941
+ `and ${initiativeCount} initiative${initiativeCount === 1 ? "" : "s"}`
8942
+ ] : []
8943
+ ];
8944
+ return formatConfirmation(
8945
+ summaryParts.join(" "),
8946
+ [
8947
+ 'Tasks created with "suggested" status \u2014 human will approve or reject each',
8948
+ ...initiativeCount > 0 ? [
8949
+ "Initiative suggestions created \u2014 human will approve or reject independently"
8950
+ ] : [],
8951
+ "Use deskfree_state to check for approved tasks"
8952
+ ],
8953
+ result
8954
+ );
8955
+ } catch (err) {
8956
+ return errorResult(err);
8957
+ }
8958
+ };
8959
+ }
8960
+ function makeClaimEvaluationHandler(client) {
8961
+ return async (_id, params) => {
8962
+ try {
8963
+ const taskId = validateStringParam(params, "taskId", true);
8964
+ const result = await client.claimEvaluation({ taskId });
8965
+ if (!result) {
8966
+ return formatConfirmation(
8967
+ "Evaluation already claimed by another process",
8968
+ ["Use deskfree_state to check for other pending evaluations"]
8969
+ );
8970
+ }
8971
+ const state = await client.getState().catch(() => null);
8972
+ return {
8973
+ content: [
8974
+ {
8975
+ type: "text",
8976
+ text: JSON.stringify(
8977
+ {
8978
+ summary: `Claimed evaluation for task "${result.task.title}"`,
8979
+ nextActions: [
8980
+ "Review the task messages, current global ways of working, and initiative content (if present)",
8981
+ "Decide what to update: globalWoW (universal patterns), initiative (area-specific learnings), both, or neither",
8982
+ "Call deskfree_submit_evaluation with your analysis"
8983
+ ],
8984
+ task: result.task,
8985
+ waysOfWorking: result.waysOfWorking,
8986
+ currentVersion: result.currentVersion,
8987
+ messages: result.messages,
8988
+ ...result.initiative ? { initiative: result.initiative } : {},
8989
+ ...state ? { workspaceState: state } : {}
8990
+ },
8991
+ null,
8992
+ 2
8993
+ )
8994
+ }
8995
+ ]
8996
+ };
8997
+ } catch (err) {
8998
+ return errorResult(err);
8999
+ }
9000
+ };
9001
+ }
9002
+ function makeSubmitEvaluationHandler(client, followUpHint) {
9003
+ return async (_id, params) => {
9004
+ try {
9005
+ const taskId = validateStringParam(params, "taskId", true);
9006
+ const reasoning = validateStringParam(params, "reasoning", true);
9007
+ const globalWoW = parseEvaluationSection(params?.globalWoW, "globalWoW");
9008
+ const initiative = parseEvaluationSection(
9009
+ params?.initiative,
9010
+ "initiative"
9011
+ );
9012
+ const result = await client.submitEvaluation({
9013
+ taskId,
9014
+ reasoning,
9015
+ globalWoW,
9016
+ initiative
9017
+ });
9018
+ const parts = [];
9019
+ if (globalWoW.hasChanges)
9020
+ parts.push(`global WoW \u2192 v${result.globalVersion}`);
9021
+ if (initiative.hasChanges && result.initiativeVersion !== void 0) {
9022
+ parts.push(`initiative \u2192 v${result.initiativeVersion}`);
9023
+ }
9024
+ const messageContent = parts.length > 0 ? `\u{1F4DD} Updated ${parts.join(", ")}: ${reasoning}` : `\u{1F4DD} No updates to ways of working: ${reasoning}`;
9025
+ await client.sendMessage({ content: messageContent, taskId }).catch(() => {
9026
+ });
9027
+ const summaryParts = [];
9028
+ if (globalWoW.hasChanges)
9029
+ summaryParts.push(`global WoW v${result.globalVersion}`);
9030
+ if (initiative.hasChanges && result.initiativeVersion !== void 0) {
9031
+ summaryParts.push(`initiative v${result.initiativeVersion}`);
9032
+ }
9033
+ return formatConfirmation(
9034
+ summaryParts.length > 0 ? `Evaluation complete \u2014 updated ${summaryParts.join(", ")}` : "Evaluation complete \u2014 no changes needed",
9035
+ [followUpHint],
9036
+ result
9037
+ );
9038
+ } catch (err) {
9039
+ return errorResult(err);
9040
+ }
9041
+ };
9042
+ }
8619
9043
  function createOrchestratorTools(api) {
8620
9044
  const account = resolveAccountFromConfig(api);
8621
9045
  if (!account) return null;
@@ -8634,37 +9058,6 @@ function createOrchestratorTools(api) {
8634
9058
  }
8635
9059
  }
8636
9060
  },
8637
- {
8638
- ...ORCHESTRATOR_TOOLS.CREATE_TASK,
8639
- async execute(_id, params) {
8640
- try {
8641
- const title = validateStringParam(params, "title", true);
8642
- const instructions = validateStringParam(
8643
- params,
8644
- "instructions",
8645
- false
8646
- );
8647
- const task = await client.createTask({
8648
- title,
8649
- instructions
8650
- });
8651
- await client.sendMessage({
8652
- content: `\u{1F4CB} Created task: "${task.title}"`
8653
- }).catch(() => {
8654
- });
8655
- return formatTaskResponse(
8656
- task,
8657
- `Created task "${task.title}" with ID ${task.taskId}`,
8658
- [
8659
- "Claim and start this task with deskfree_start_task",
8660
- "Or check workspace state with deskfree_state"
8661
- ]
8662
- );
8663
- } catch (err) {
8664
- return errorResult(err);
8665
- }
8666
- }
8667
- },
8668
9061
  {
8669
9062
  ...ORCHESTRATOR_TOOLS.START_TASK,
8670
9063
  async execute(_id, params) {
@@ -8681,8 +9074,10 @@ function createOrchestratorTools(api) {
8681
9074
  summary: `Claimed task "${result.title}" \u2014 full context loaded`,
8682
9075
  nextActions: [
8683
9076
  "Read the instructions and message history carefully",
8684
- "Update deliverable incrementally with deskfree_update_deliverable",
8685
- "Complete with deskfree_complete_task when done"
9077
+ ...result.fileContext ? [
9078
+ `Task has a linked file "${result.fileContext.name}" (ID: ${result.fileContext.fileId}) \u2014 use deskfree_update_file to save your work to it`
9079
+ ] : [],
9080
+ "Complete with deskfree_complete_task when done (summary required)"
8686
9081
  ],
8687
9082
  task: result
8688
9083
  },
@@ -8698,187 +9093,35 @@ function createOrchestratorTools(api) {
8698
9093
  }
8699
9094
  },
8700
9095
  {
8701
- ...ORCHESTRATOR_TOOLS.UPDATE_DELIVERABLE,
8702
- async execute(_id, params) {
8703
- try {
8704
- const taskId = validateStringParam(params, "taskId", true);
8705
- const deliverable = validateStringParam(params, "deliverable", true);
8706
- const format = validateEnumParam(
8707
- params,
8708
- "format",
8709
- ["markdown", "html"],
8710
- false
8711
- );
8712
- await client.updateDeliverable({ taskId, deliverable, format });
8713
- return formatConfirmation(`Updated deliverable for task ${taskId}`, [
8714
- "Deliverable has been saved",
8715
- "Complete with deskfree_complete_task when ready"
8716
- ]);
8717
- } catch (err) {
8718
- return errorResult(err);
8719
- }
8720
- }
9096
+ ...ORCHESTRATOR_TOOLS.UPDATE_FILE,
9097
+ execute: makeUpdateFileHandler(client)
9098
+ },
9099
+ {
9100
+ ...ORCHESTRATOR_TOOLS.CREATE_FILE,
9101
+ execute: makeCreateFileHandler(client)
8721
9102
  },
8722
9103
  {
8723
9104
  ...ORCHESTRATOR_TOOLS.COMPLETE_TASK,
8724
- async execute(_id, params) {
8725
- try {
8726
- const taskId = validateStringParam(params, "taskId", true);
8727
- const outcome = validateEnumParam(
8728
- params,
8729
- "outcome",
8730
- ["done", "blocked"],
8731
- true
8732
- );
8733
- const result = await client.completeTask({ taskId, outcome });
8734
- setActiveTaskId(null);
8735
- const summaryVerb = outcome === "done" ? "completed" : "blocked";
8736
- const icon = outcome === "done" ? "\u2705" : "\u{1F6AB}";
8737
- await client.sendMessage({
8738
- content: `${icon} Task ${summaryVerb}: "${result.title}"`
8739
- }).catch(() => {
8740
- });
8741
- return formatTaskResponse(
8742
- result,
8743
- `Task "${result.title}" marked as ${summaryVerb} \u2014 waiting for human`,
8744
- [
8745
- outcome === "done" ? "Human will review the deliverable" : "Human will review the blocker and provide guidance",
8746
- "Use deskfree_state to check for other tasks"
8747
- ]
8748
- );
8749
- } catch (err) {
8750
- return errorResult(err);
8751
- }
8752
- }
9105
+ execute: makeCompleteTaskHandler(client)
8753
9106
  },
8754
9107
  {
8755
9108
  ...ORCHESTRATOR_TOOLS.SEND_MESSAGE,
8756
- async execute(_id, params) {
8757
- try {
8758
- const content = validateStringParam(params, "content", false);
8759
- const taskId = validateStringParam(params, "taskId", false);
8760
- const rawSuggestions = params?.suggestions;
8761
- let suggestions;
8762
- if (Array.isArray(rawSuggestions) && rawSuggestions.length > 0) {
8763
- suggestions = rawSuggestions.map((t, i) => {
8764
- if (typeof t !== "object" || t === null) {
8765
- throw new Error(`suggestions[${i}] must be an object`);
8766
- }
8767
- const item = t;
8768
- const title = item["title"];
8769
- if (typeof title !== "string" || title.trim() === "") {
8770
- throw new Error(
8771
- `suggestions[${i}].title must be a non-empty string`
8772
- );
8773
- }
8774
- const instructions = item["instructions"];
8775
- return {
8776
- title: title.trim(),
8777
- instructions: typeof instructions === "string" ? instructions : void 0
8778
- };
8779
- });
8780
- }
8781
- if (!content && !suggestions) {
8782
- throw new Error(
8783
- 'Either "content" or "suggestions" parameter is required'
8784
- );
8785
- }
8786
- if (suggestions) {
8787
- await client.sendMessage({ suggestions, taskId });
8788
- return formatConfirmation(
8789
- `Suggested ${suggestions.length} task${suggestions.length === 1 ? "" : "s"} for human review`,
8790
- [
8791
- "The human will see approve/reject buttons for each suggestion",
8792
- "Continue working on the current task"
8793
- ]
8794
- );
8795
- }
8796
- await client.sendMessage({ content, taskId });
8797
- return formatConfirmation(
8798
- `Message sent${taskId ? ` to task ${taskId}` : ""}`,
8799
- [
8800
- "Message delivered to the human",
8801
- taskId ? "Continue working on the task or wait for response" : "Check for response with task messages"
8802
- ]
8803
- );
8804
- } catch (err) {
8805
- return errorResult(err);
8806
- }
8807
- }
9109
+ execute: makeSendMessageHandler(client)
9110
+ },
9111
+ {
9112
+ ...ORCHESTRATOR_TOOLS.SUGGEST_TASKS,
9113
+ execute: makeSuggestTasksHandler(client)
8808
9114
  },
8809
9115
  {
8810
9116
  ...ORCHESTRATOR_TOOLS.CLAIM_EVALUATION,
8811
- async execute(_id, params) {
8812
- try {
8813
- const taskId = validateStringParam(params, "taskId", true);
8814
- const result = await client.claimEvaluation({ taskId });
8815
- if (!result) {
8816
- return formatConfirmation(
8817
- "Evaluation already claimed by another process",
8818
- ["Use deskfree_state to check for other pending evaluations"]
8819
- );
8820
- }
8821
- return {
8822
- content: [
8823
- {
8824
- type: "text",
8825
- text: JSON.stringify(
8826
- {
8827
- summary: `Claimed evaluation for task "${result.task.title}"`,
8828
- nextActions: [
8829
- "Review the task messages and current ways of working",
8830
- "Decide if patterns/learnings should update ways of working",
8831
- "Call deskfree_submit_evaluation with your analysis"
8832
- ],
8833
- task: result.task,
8834
- waysOfWorking: result.waysOfWorking,
8835
- currentVersion: result.currentVersion,
8836
- messages: result.messages
8837
- },
8838
- null,
8839
- 2
8840
- )
8841
- }
8842
- ]
8843
- };
8844
- } catch (err) {
8845
- return errorResult(err);
8846
- }
8847
- }
9117
+ execute: makeClaimEvaluationHandler(client)
8848
9118
  },
8849
9119
  {
8850
9120
  ...ORCHESTRATOR_TOOLS.SUBMIT_EVALUATION,
8851
- async execute(_id, params) {
8852
- try {
8853
- const taskId = validateStringParam(params, "taskId", true);
8854
- const reasoning = validateStringParam(params, "reasoning", true);
8855
- const hasChanges = params?.hasChanges;
8856
- if (typeof hasChanges !== "boolean") {
8857
- throw new Error('Parameter "hasChanges" must be a boolean');
8858
- }
8859
- const updatedContent = validateStringParam(
8860
- params,
8861
- "updatedContent",
8862
- false
8863
- );
8864
- const result = await client.submitEvaluation({
8865
- taskId,
8866
- reasoning,
8867
- hasChanges,
8868
- updatedContent
8869
- });
8870
- const messageContent = hasChanges ? `\u{1F4DD} Updated ways of working (v${result.version}): ${reasoning}` : `\u{1F4DD} No updates to ways of working: ${reasoning}`;
8871
- await client.sendMessage({ content: messageContent, taskId }).catch(() => {
8872
- });
8873
- return formatConfirmation(
8874
- hasChanges ? `Ways of working updated to v${result.version}` : "Evaluation complete \u2014 no changes needed",
8875
- ["Use deskfree_state to check for other pending evaluations"],
8876
- result
8877
- );
8878
- } catch (err) {
8879
- return errorResult(err);
8880
- }
8881
- }
9121
+ execute: makeSubmitEvaluationHandler(
9122
+ client,
9123
+ "Use deskfree_state to check for other pending evaluations"
9124
+ )
8882
9125
  }
8883
9126
  ];
8884
9127
  }