@questionbase/deskfree 0.3.0-alpha.21 → 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/README.md +24 -3
- package/dist/index.d.ts +66 -28
- package/dist/index.js +466 -401
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/deskfree/SKILL.md +225 -103
- package/skills/deskfree/references/tools.md +43 -40
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
|
|
3860
|
+
* Send a text message to a DeskFree conversation.
|
|
3861
3861
|
*
|
|
3862
|
-
* @param input - Message content, optional userId, taskId, attachments
|
|
3862
|
+
* @param input - Message content, optional userId, taskId, attachments
|
|
3863
3863
|
*/
|
|
3864
3864
|
async sendMessage(input) {
|
|
3865
|
-
|
|
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. */
|
|
@@ -3894,11 +3887,25 @@ var DeskFreeClient = class {
|
|
|
3894
3887
|
this.requireNonEmpty(input.taskId, "taskId");
|
|
3895
3888
|
return this.request("POST", "tasks.claim", input);
|
|
3896
3889
|
}
|
|
3897
|
-
/**
|
|
3898
|
-
async
|
|
3890
|
+
/** Fetch a lightweight task summary by ID. Read-only, no side effects. */
|
|
3891
|
+
async getTask(input) {
|
|
3899
3892
|
this.requireNonEmpty(input.taskId, "taskId");
|
|
3900
|
-
this.
|
|
3901
|
-
|
|
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", {});
|
|
3902
3909
|
}
|
|
3903
3910
|
/** Send an agent status update to DeskFree. */
|
|
3904
3911
|
async statusUpdate(input) {
|
|
@@ -3923,21 +3930,6 @@ var DeskFreeClient = class {
|
|
|
3923
3930
|
this.requireNonEmpty(input.taskId, "taskId");
|
|
3924
3931
|
return this.request("POST", "tasks.complete", input);
|
|
3925
3932
|
}
|
|
3926
|
-
/** Suggest new tasks for the human to review and approve (via messages.send with suggestions). */
|
|
3927
|
-
async suggestTasks(input) {
|
|
3928
|
-
if (!input.tasks || input.tasks.length === 0) {
|
|
3929
|
-
throw new DeskFreeError(
|
|
3930
|
-
"client",
|
|
3931
|
-
"tasks",
|
|
3932
|
-
"tasks array is required and cannot be empty",
|
|
3933
|
-
"Missing required parameter: tasks. Please provide at least one task to suggest."
|
|
3934
|
-
);
|
|
3935
|
-
}
|
|
3936
|
-
return this.sendMessage({
|
|
3937
|
-
suggestions: input.tasks,
|
|
3938
|
-
taskId: input.taskId
|
|
3939
|
-
});
|
|
3940
|
-
}
|
|
3941
3933
|
/** Suggest tasks via the dedicated bot/tasks.suggest endpoint. */
|
|
3942
3934
|
async suggestTasksDedicated(input) {
|
|
3943
3935
|
if (!input.suggestions || input.suggestions.length === 0) {
|
|
@@ -4429,6 +4421,20 @@ async function fetchAndSaveMedia(attachment) {
|
|
|
4429
4421
|
}
|
|
4430
4422
|
}
|
|
4431
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
|
+
}
|
|
4432
4438
|
async function deliverMessageToAgent(ctx, message, client) {
|
|
4433
4439
|
const runtime = getDeskFreeRuntime();
|
|
4434
4440
|
const log = ctx.log ?? runtime.logging.createLogger("deskfree:deliver");
|
|
@@ -4471,9 +4477,20 @@ async function deliverMessageToAgent(ctx, message, client) {
|
|
|
4471
4477
|
);
|
|
4472
4478
|
}
|
|
4473
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
|
+
}
|
|
4474
4490
|
const msgCtx = runtime.channel.reply.finalizeInboundContext({
|
|
4475
4491
|
Body: message.content,
|
|
4476
4492
|
RawBody: message.content,
|
|
4493
|
+
...bodyForAgent ? { BodyForAgent: bodyForAgent } : {},
|
|
4477
4494
|
ChatType: "dm",
|
|
4478
4495
|
Provider: "deskfree",
|
|
4479
4496
|
Surface: "deskfree",
|
|
@@ -4585,6 +4602,7 @@ var wrapper_default = import_websocket.default;
|
|
|
4585
4602
|
// src/gateway.ts
|
|
4586
4603
|
var activeTaskId = null;
|
|
4587
4604
|
var completedTaskId = null;
|
|
4605
|
+
var inboundThreadId = null;
|
|
4588
4606
|
function setActiveTaskId(taskId) {
|
|
4589
4607
|
if (taskId === null && activeTaskId !== null) {
|
|
4590
4608
|
completedTaskId = activeTaskId;
|
|
@@ -4592,15 +4610,19 @@ function setActiveTaskId(taskId) {
|
|
|
4592
4610
|
activeTaskId = taskId;
|
|
4593
4611
|
}
|
|
4594
4612
|
function getActiveTaskId() {
|
|
4595
|
-
return activeTaskId ?? completedTaskId;
|
|
4613
|
+
return activeTaskId ?? completedTaskId ?? inboundThreadId;
|
|
4596
4614
|
}
|
|
4597
4615
|
function clearCompletedTaskId() {
|
|
4598
4616
|
completedTaskId = null;
|
|
4599
4617
|
}
|
|
4618
|
+
function setInboundThreadId(taskId) {
|
|
4619
|
+
inboundThreadId = taskId;
|
|
4620
|
+
}
|
|
4600
4621
|
var PING_INTERVAL_MS = 5 * 60 * 1e3;
|
|
4601
4622
|
var POLL_FALLBACK_INTERVAL_MS = 30 * 1e3;
|
|
4602
4623
|
var WS_CONNECTION_TIMEOUT_MS = 30 * 1e3;
|
|
4603
4624
|
var WS_PONG_TIMEOUT_MS = 10 * 1e3;
|
|
4625
|
+
var NOTIFY_DEBOUNCE_MS = 200;
|
|
4604
4626
|
var BACKOFF_INITIAL_MS = 2e3;
|
|
4605
4627
|
var BACKOFF_MAX_MS = 3e4;
|
|
4606
4628
|
var BACKOFF_FACTOR = 1.8;
|
|
@@ -4824,6 +4846,7 @@ async function runWebSocketConnection(opts) {
|
|
|
4824
4846
|
let pingInterval;
|
|
4825
4847
|
let connectionTimer;
|
|
4826
4848
|
let pongTimer;
|
|
4849
|
+
let notifyDebounceTimer;
|
|
4827
4850
|
let isConnected = false;
|
|
4828
4851
|
const cleanup = () => {
|
|
4829
4852
|
if (pingInterval !== void 0) {
|
|
@@ -4838,6 +4861,10 @@ async function runWebSocketConnection(opts) {
|
|
|
4838
4861
|
clearTimeout(pongTimer);
|
|
4839
4862
|
pongTimer = void 0;
|
|
4840
4863
|
}
|
|
4864
|
+
if (notifyDebounceTimer !== void 0) {
|
|
4865
|
+
clearTimeout(notifyDebounceTimer);
|
|
4866
|
+
notifyDebounceTimer = void 0;
|
|
4867
|
+
}
|
|
4841
4868
|
};
|
|
4842
4869
|
connectionTimer = setTimeout(() => {
|
|
4843
4870
|
if (!isConnected) {
|
|
@@ -4918,15 +4945,21 @@ async function runWebSocketConnection(opts) {
|
|
|
4918
4945
|
return;
|
|
4919
4946
|
}
|
|
4920
4947
|
if (msg.action === "notify") {
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
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);
|
|
4930
4963
|
} else if (msg.action === "pong") {
|
|
4931
4964
|
if (pongTimer !== void 0) {
|
|
4932
4965
|
clearTimeout(pongTimer);
|
|
@@ -5123,6 +5156,7 @@ async function pollAndDeliver(client, ctx, cursor, log, account) {
|
|
|
5123
5156
|
continue;
|
|
5124
5157
|
}
|
|
5125
5158
|
clearCompletedTaskId();
|
|
5159
|
+
setInboundThreadId(message.taskId ?? null);
|
|
5126
5160
|
await deliverMessageToAgent(ctx, message, client);
|
|
5127
5161
|
deliveredMessageIds.add(message.messageId);
|
|
5128
5162
|
deliveredCount++;
|
|
@@ -7767,66 +7801,72 @@ var ORCHESTRATOR_TOOLS = {
|
|
|
7767
7801
|
},
|
|
7768
7802
|
START_TASK: {
|
|
7769
7803
|
name: "deskfree_start_task",
|
|
7770
|
-
description: "Claim a bot task (isWorking=false) and start working. Returns full context (instructions,
|
|
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).",
|
|
7771
7805
|
parameters: Type.Object({
|
|
7772
7806
|
taskId: Type.String({ description: "Task UUID to claim" })
|
|
7773
7807
|
})
|
|
7774
7808
|
},
|
|
7775
|
-
|
|
7776
|
-
name: "
|
|
7777
|
-
description:
|
|
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.`,
|
|
7778
7812
|
parameters: Type.Object({
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
description: "
|
|
7813
|
+
fileId: Type.String({ description: "File ID to update" }),
|
|
7814
|
+
content: Type.String({
|
|
7815
|
+
description: "Full file content (replaces previous)"
|
|
7782
7816
|
}),
|
|
7783
|
-
|
|
7817
|
+
contentFormat: Type.Optional(
|
|
7784
7818
|
Type.Union([Type.Literal("markdown"), Type.Literal("html")], {
|
|
7785
|
-
description: '"markdown" (default) for text/documents, "html" for rich web content.
|
|
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"'
|
|
7786
7840
|
})
|
|
7787
7841
|
)
|
|
7788
7842
|
})
|
|
7789
7843
|
},
|
|
7790
7844
|
COMPLETE_TASK: {
|
|
7791
7845
|
name: "deskfree_complete_task",
|
|
7792
|
-
description: 'Finish a task. Outcome "done" = work complete
|
|
7846
|
+
description: 'Finish a task. Outcome "done" = work complete (summary required). Outcome "blocked" = need human input. Both move to human.',
|
|
7793
7847
|
parameters: Type.Object({
|
|
7794
7848
|
taskId: Type.String({ description: "Task UUID" }),
|
|
7795
7849
|
outcome: Type.Union([Type.Literal("done"), Type.Literal("blocked")], {
|
|
7796
7850
|
description: '"done" = work complete, "blocked" = need human input'
|
|
7797
|
-
})
|
|
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
|
+
)
|
|
7798
7857
|
})
|
|
7799
7858
|
},
|
|
7800
7859
|
SEND_MESSAGE: {
|
|
7801
7860
|
name: "deskfree_send_message",
|
|
7802
|
-
description: "Send a message in the task thread (progress update, question, status report).
|
|
7861
|
+
description: "Send a message in the task thread (progress update, question, status report).",
|
|
7803
7862
|
parameters: Type.Object({
|
|
7804
|
-
content: Type.
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
})
|
|
7808
|
-
),
|
|
7863
|
+
content: Type.String({
|
|
7864
|
+
description: "Message content."
|
|
7865
|
+
}),
|
|
7809
7866
|
taskId: Type.Optional(
|
|
7810
7867
|
Type.String({
|
|
7811
7868
|
description: "Task UUID (optional if context provides it)"
|
|
7812
7869
|
})
|
|
7813
|
-
),
|
|
7814
|
-
suggestions: Type.Optional(
|
|
7815
|
-
Type.Array(
|
|
7816
|
-
Type.Object({
|
|
7817
|
-
title: Type.String({ description: "Task title (max 200 chars)" }),
|
|
7818
|
-
instructions: Type.Optional(
|
|
7819
|
-
Type.String({
|
|
7820
|
-
description: "Detailed instructions for the suggested task"
|
|
7821
|
-
})
|
|
7822
|
-
)
|
|
7823
|
-
}),
|
|
7824
|
-
{
|
|
7825
|
-
description: "Suggest tasks for human review (1-10). The human will see approve/reject buttons for each. Provide this instead of content.",
|
|
7826
|
-
minItems: 1,
|
|
7827
|
-
maxItems: 10
|
|
7828
|
-
}
|
|
7829
|
-
)
|
|
7830
7870
|
)
|
|
7831
7871
|
})
|
|
7832
7872
|
},
|
|
@@ -7849,15 +7889,35 @@ var ORCHESTRATOR_TOOLS = {
|
|
|
7849
7889
|
description: "Estimated token cost \u2014 consider files to read, reasoning, output"
|
|
7850
7890
|
})
|
|
7851
7891
|
),
|
|
7852
|
-
dependsOn: Type.Optional(
|
|
7853
|
-
Type.Array(Type.String(), {
|
|
7854
|
-
description: "Task IDs this suggestion depends on (blocks claiming until those are done)"
|
|
7855
|
-
})
|
|
7856
|
-
),
|
|
7857
7892
|
initiativeId: Type.Optional(
|
|
7858
7893
|
Type.String({
|
|
7859
7894
|
description: "Link to existing initiative ID \u2014 set when this task belongs to an active initiative"
|
|
7860
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
|
+
)
|
|
7861
7921
|
)
|
|
7862
7922
|
}),
|
|
7863
7923
|
{
|
|
@@ -7889,7 +7949,7 @@ var ORCHESTRATOR_TOOLS = {
|
|
|
7889
7949
|
{
|
|
7890
7950
|
description: 'Propose new initiatives (optional). Created with "suggested" status \u2014 human approves or rejects independently.',
|
|
7891
7951
|
minItems: 1,
|
|
7892
|
-
maxItems:
|
|
7952
|
+
maxItems: 5
|
|
7893
7953
|
}
|
|
7894
7954
|
)
|
|
7895
7955
|
)
|
|
@@ -7944,9 +8004,12 @@ var ORCHESTRATOR_TOOLS = {
|
|
|
7944
8004
|
}
|
|
7945
8005
|
};
|
|
7946
8006
|
var WORKER_TOOLS = {
|
|
7947
|
-
|
|
8007
|
+
UPDATE_FILE: ORCHESTRATOR_TOOLS.UPDATE_FILE,
|
|
8008
|
+
CREATE_FILE: ORCHESTRATOR_TOOLS.CREATE_FILE,
|
|
7948
8009
|
COMPLETE_TASK: ORCHESTRATOR_TOOLS.COMPLETE_TASK,
|
|
7949
8010
|
SEND_MESSAGE: ORCHESTRATOR_TOOLS.SEND_MESSAGE,
|
|
8011
|
+
SUGGEST_TASKS: ORCHESTRATOR_TOOLS.SUGGEST_TASKS,
|
|
8012
|
+
CLAIM_EVALUATION: ORCHESTRATOR_TOOLS.CLAIM_EVALUATION,
|
|
7950
8013
|
SUBMIT_EVALUATION: ORCHESTRATOR_TOOLS.SUBMIT_EVALUATION
|
|
7951
8014
|
};
|
|
7952
8015
|
var CHANNEL_META = {
|
|
@@ -8506,20 +8569,20 @@ var deskFreePlugin = {
|
|
|
8506
8569
|
// src/context.ts
|
|
8507
8570
|
var DESKFREE_AGENT_DIRECTIVE = `## DeskFree \u2014 The Work Loop
|
|
8508
8571
|
Always read the deskfree skill (SKILL.md) at startup. Follow the suggest-first work loop:
|
|
8509
|
-
1. **Check state** \u2192 \`deskfree_state\` \u2014 see what needs attention, read ways of working.
|
|
8510
|
-
2. **Suggest tasks** \u2192 \`deskfree_suggest_tasks\` \u2014 ALL work requires human-approved tasks first.
|
|
8511
|
-
3. **Claim a task** \u2192 \`deskfree_start_task\` \u2014 read instructions + parent context
|
|
8512
|
-
4. **Do the work** \u2192 \`
|
|
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.
|
|
8513
8576
|
5. **Suggest follow-ups** \u2192 if work reveals more to do, suggest them (strongest when you have full context).
|
|
8514
|
-
6. **Complete** \u2192 \`deskfree_complete_task\` \u2014
|
|
8577
|
+
6. **Complete** \u2192 \`deskfree_complete_task\` \u2014 summary required for outcome "done".
|
|
8515
8578
|
7. **Evaluate** \u2192 check \`pendingEvaluations\` and update ways of working.
|
|
8516
8579
|
|
|
8517
8580
|
Key principles:
|
|
8518
|
-
- You're building a chain. Your instructions become someone else's brief. Your
|
|
8581
|
+
- You're building a chain. Your instructions become someone else's brief. Your file output becomes someone else's context.
|
|
8519
8582
|
- Write instructions as if briefing a contractor who has never seen the codebase.
|
|
8520
|
-
- MUST
|
|
8583
|
+
- MUST provide summary when completing with outcome "done".
|
|
8521
8584
|
- Estimate token cost per suggestion \u2014 consider files to read, reasoning, output.
|
|
8522
|
-
- Sub-agents get
|
|
8585
|
+
- Sub-agents get 7 tools: update_file, create_file, complete_task, send_message, suggest_tasks, claim_evaluation, submit_evaluation.`;
|
|
8523
8586
|
function getDeskFreeContext() {
|
|
8524
8587
|
return `${DESKFREE_AGENT_DIRECTIVE}
|
|
8525
8588
|
|
|
@@ -8558,15 +8621,7 @@ function formatTaskResponse(task, summary, nextActions) {
|
|
|
8558
8621
|
content: [
|
|
8559
8622
|
{
|
|
8560
8623
|
type: "text",
|
|
8561
|
-
text: JSON.stringify(
|
|
8562
|
-
{
|
|
8563
|
-
summary,
|
|
8564
|
-
nextActions,
|
|
8565
|
-
task
|
|
8566
|
-
},
|
|
8567
|
-
null,
|
|
8568
|
-
2
|
|
8569
|
-
)
|
|
8624
|
+
text: JSON.stringify({ summary, nextActions, task }, null, 2)
|
|
8570
8625
|
}
|
|
8571
8626
|
]
|
|
8572
8627
|
};
|
|
@@ -8577,11 +8632,7 @@ function formatConfirmation(summary, nextActions, data) {
|
|
|
8577
8632
|
{
|
|
8578
8633
|
type: "text",
|
|
8579
8634
|
text: JSON.stringify(
|
|
8580
|
-
{
|
|
8581
|
-
summary,
|
|
8582
|
-
nextActions,
|
|
8583
|
-
...data ? { data } : {}
|
|
8584
|
-
},
|
|
8635
|
+
{ summary, nextActions, ...data ? { data } : {} },
|
|
8585
8636
|
null,
|
|
8586
8637
|
2
|
|
8587
8638
|
)
|
|
@@ -8642,14 +8693,7 @@ function errorResult(err) {
|
|
|
8642
8693
|
content: [
|
|
8643
8694
|
{
|
|
8644
8695
|
type: "text",
|
|
8645
|
-
text: JSON.stringify(
|
|
8646
|
-
{
|
|
8647
|
-
error: true,
|
|
8648
|
-
message: rawMessage
|
|
8649
|
-
},
|
|
8650
|
-
null,
|
|
8651
|
-
2
|
|
8652
|
-
)
|
|
8696
|
+
text: JSON.stringify({ error: true, message: rawMessage }, null, 2)
|
|
8653
8697
|
}
|
|
8654
8698
|
],
|
|
8655
8699
|
isError: true
|
|
@@ -8666,9 +8710,7 @@ function validateStringParam(params, key, required) {
|
|
|
8666
8710
|
}
|
|
8667
8711
|
const value = params[key];
|
|
8668
8712
|
if (value == null) {
|
|
8669
|
-
if (required) {
|
|
8670
|
-
throw new Error(`Required parameter '${key}' is missing`);
|
|
8671
|
-
}
|
|
8713
|
+
if (required) throw new Error(`Required parameter '${key}' is missing`);
|
|
8672
8714
|
return void 0;
|
|
8673
8715
|
}
|
|
8674
8716
|
if (typeof value !== "string") {
|
|
@@ -8690,9 +8732,7 @@ function validateEnumParam(params, key, allowedValues, required) {
|
|
|
8690
8732
|
}
|
|
8691
8733
|
const value = params[key];
|
|
8692
8734
|
if (value == null) {
|
|
8693
|
-
if (required) {
|
|
8694
|
-
throw new Error(`Required parameter '${key}' is missing`);
|
|
8695
|
-
}
|
|
8735
|
+
if (required) throw new Error(`Required parameter '${key}' is missing`);
|
|
8696
8736
|
return void 0;
|
|
8697
8737
|
}
|
|
8698
8738
|
if (typeof value !== "string") {
|
|
@@ -8708,6 +8748,298 @@ function validateEnumParam(params, key, allowedValues, required) {
|
|
|
8708
8748
|
}
|
|
8709
8749
|
return value;
|
|
8710
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
|
+
}
|
|
8711
9043
|
function createOrchestratorTools(api) {
|
|
8712
9044
|
const account = resolveAccountFromConfig(api);
|
|
8713
9045
|
if (!account) return null;
|
|
@@ -8742,8 +9074,10 @@ function createOrchestratorTools(api) {
|
|
|
8742
9074
|
summary: `Claimed task "${result.title}" \u2014 full context loaded`,
|
|
8743
9075
|
nextActions: [
|
|
8744
9076
|
"Read the instructions and message history carefully",
|
|
8745
|
-
|
|
8746
|
-
|
|
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)"
|
|
8747
9081
|
],
|
|
8748
9082
|
task: result
|
|
8749
9083
|
},
|
|
@@ -8759,304 +9093,35 @@ function createOrchestratorTools(api) {
|
|
|
8759
9093
|
}
|
|
8760
9094
|
},
|
|
8761
9095
|
{
|
|
8762
|
-
...ORCHESTRATOR_TOOLS.
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
params,
|
|
8769
|
-
"format",
|
|
8770
|
-
["markdown", "html"],
|
|
8771
|
-
false
|
|
8772
|
-
);
|
|
8773
|
-
await client.updateDeliverable({ taskId, deliverable, format });
|
|
8774
|
-
return formatConfirmation(`Updated deliverable for task ${taskId}`, [
|
|
8775
|
-
"Deliverable has been saved",
|
|
8776
|
-
"Complete with deskfree_complete_task when ready"
|
|
8777
|
-
]);
|
|
8778
|
-
} catch (err) {
|
|
8779
|
-
return errorResult(err);
|
|
8780
|
-
}
|
|
8781
|
-
}
|
|
9096
|
+
...ORCHESTRATOR_TOOLS.UPDATE_FILE,
|
|
9097
|
+
execute: makeUpdateFileHandler(client)
|
|
9098
|
+
},
|
|
9099
|
+
{
|
|
9100
|
+
...ORCHESTRATOR_TOOLS.CREATE_FILE,
|
|
9101
|
+
execute: makeCreateFileHandler(client)
|
|
8782
9102
|
},
|
|
8783
9103
|
{
|
|
8784
9104
|
...ORCHESTRATOR_TOOLS.COMPLETE_TASK,
|
|
8785
|
-
|
|
8786
|
-
try {
|
|
8787
|
-
const taskId = validateStringParam(params, "taskId", true);
|
|
8788
|
-
const outcome = validateEnumParam(
|
|
8789
|
-
params,
|
|
8790
|
-
"outcome",
|
|
8791
|
-
["done", "blocked"],
|
|
8792
|
-
true
|
|
8793
|
-
);
|
|
8794
|
-
const result = await client.completeTask({ taskId, outcome });
|
|
8795
|
-
setActiveTaskId(null);
|
|
8796
|
-
const summaryVerb = outcome === "done" ? "completed" : "blocked";
|
|
8797
|
-
const icon = outcome === "done" ? "\u2705" : "\u{1F6AB}";
|
|
8798
|
-
await client.sendMessage({
|
|
8799
|
-
content: `${icon} Task ${summaryVerb}: "${result.title}"`
|
|
8800
|
-
}).catch(() => {
|
|
8801
|
-
});
|
|
8802
|
-
return formatTaskResponse(
|
|
8803
|
-
result,
|
|
8804
|
-
`Task "${result.title}" marked as ${summaryVerb} \u2014 waiting for human`,
|
|
8805
|
-
[
|
|
8806
|
-
outcome === "done" ? "Human will review the deliverable" : "Human will review the blocker and provide guidance",
|
|
8807
|
-
"Use deskfree_state to check for other tasks"
|
|
8808
|
-
]
|
|
8809
|
-
);
|
|
8810
|
-
} catch (err) {
|
|
8811
|
-
return errorResult(err);
|
|
8812
|
-
}
|
|
8813
|
-
}
|
|
9105
|
+
execute: makeCompleteTaskHandler(client)
|
|
8814
9106
|
},
|
|
8815
9107
|
{
|
|
8816
9108
|
...ORCHESTRATOR_TOOLS.SEND_MESSAGE,
|
|
8817
|
-
|
|
8818
|
-
try {
|
|
8819
|
-
const content = validateStringParam(params, "content", false);
|
|
8820
|
-
const taskId = validateStringParam(params, "taskId", false);
|
|
8821
|
-
const rawSuggestions = params?.suggestions;
|
|
8822
|
-
let suggestions;
|
|
8823
|
-
if (Array.isArray(rawSuggestions) && rawSuggestions.length > 0) {
|
|
8824
|
-
suggestions = rawSuggestions.map((t, i) => {
|
|
8825
|
-
if (typeof t !== "object" || t === null) {
|
|
8826
|
-
throw new Error(`suggestions[${i}] must be an object`);
|
|
8827
|
-
}
|
|
8828
|
-
const item = t;
|
|
8829
|
-
const title = item["title"];
|
|
8830
|
-
if (typeof title !== "string" || title.trim() === "") {
|
|
8831
|
-
throw new Error(
|
|
8832
|
-
`suggestions[${i}].title must be a non-empty string`
|
|
8833
|
-
);
|
|
8834
|
-
}
|
|
8835
|
-
const instructions = item["instructions"];
|
|
8836
|
-
return {
|
|
8837
|
-
title: title.trim(),
|
|
8838
|
-
instructions: typeof instructions === "string" ? instructions : void 0
|
|
8839
|
-
};
|
|
8840
|
-
});
|
|
8841
|
-
}
|
|
8842
|
-
if (!content && !suggestions) {
|
|
8843
|
-
throw new Error(
|
|
8844
|
-
'Either "content" or "suggestions" parameter is required'
|
|
8845
|
-
);
|
|
8846
|
-
}
|
|
8847
|
-
if (suggestions) {
|
|
8848
|
-
await client.sendMessage({ suggestions, taskId });
|
|
8849
|
-
return formatConfirmation(
|
|
8850
|
-
`Suggested ${suggestions.length} task${suggestions.length === 1 ? "" : "s"} for human review`,
|
|
8851
|
-
[
|
|
8852
|
-
"The human will see approve/reject buttons for each suggestion",
|
|
8853
|
-
"Continue working on the current task"
|
|
8854
|
-
]
|
|
8855
|
-
);
|
|
8856
|
-
}
|
|
8857
|
-
await client.sendMessage({ content, taskId });
|
|
8858
|
-
return formatConfirmation(
|
|
8859
|
-
`Message sent${taskId ? ` to task ${taskId}` : ""}`,
|
|
8860
|
-
[
|
|
8861
|
-
"Message delivered to the human",
|
|
8862
|
-
taskId ? "Continue working on the task or wait for response" : "Check for response with task messages"
|
|
8863
|
-
]
|
|
8864
|
-
);
|
|
8865
|
-
} catch (err) {
|
|
8866
|
-
return errorResult(err);
|
|
8867
|
-
}
|
|
8868
|
-
}
|
|
9109
|
+
execute: makeSendMessageHandler(client)
|
|
8869
9110
|
},
|
|
8870
9111
|
{
|
|
8871
9112
|
...ORCHESTRATOR_TOOLS.SUGGEST_TASKS,
|
|
8872
|
-
|
|
8873
|
-
try {
|
|
8874
|
-
const rawSuggestions = params?.suggestions;
|
|
8875
|
-
if (!Array.isArray(rawSuggestions) || rawSuggestions.length === 0) {
|
|
8876
|
-
throw new Error(
|
|
8877
|
-
'Parameter "suggestions" must be a non-empty array'
|
|
8878
|
-
);
|
|
8879
|
-
}
|
|
8880
|
-
const suggestions = rawSuggestions.map((s, i) => {
|
|
8881
|
-
if (typeof s !== "object" || s === null) {
|
|
8882
|
-
throw new Error(`suggestions[${i}] must be an object`);
|
|
8883
|
-
}
|
|
8884
|
-
const item = s;
|
|
8885
|
-
const title = item["title"];
|
|
8886
|
-
if (typeof title !== "string" || title.trim() === "") {
|
|
8887
|
-
throw new Error(
|
|
8888
|
-
`suggestions[${i}].title must be a non-empty string`
|
|
8889
|
-
);
|
|
8890
|
-
}
|
|
8891
|
-
return {
|
|
8892
|
-
title: title.trim(),
|
|
8893
|
-
instructions: typeof item["instructions"] === "string" ? item["instructions"] : void 0,
|
|
8894
|
-
estimatedTokens: typeof item["estimatedTokens"] === "number" ? item["estimatedTokens"] : void 0,
|
|
8895
|
-
dependsOn: Array.isArray(item["dependsOn"]) ? item["dependsOn"] : void 0,
|
|
8896
|
-
initiativeId: typeof item["initiativeId"] === "string" ? item["initiativeId"] : void 0
|
|
8897
|
-
};
|
|
8898
|
-
});
|
|
8899
|
-
const parentTaskId = validateStringParam(
|
|
8900
|
-
params,
|
|
8901
|
-
"parentTaskId",
|
|
8902
|
-
false
|
|
8903
|
-
);
|
|
8904
|
-
const rawInitiativeSuggestions = params?.initiativeSuggestions;
|
|
8905
|
-
let initiativeSuggestions;
|
|
8906
|
-
if (Array.isArray(rawInitiativeSuggestions) && rawInitiativeSuggestions.length > 0) {
|
|
8907
|
-
initiativeSuggestions = rawInitiativeSuggestions.map(
|
|
8908
|
-
(s, i) => {
|
|
8909
|
-
if (typeof s !== "object" || s === null) {
|
|
8910
|
-
throw new Error(`initiativeSuggestions[${i}] must be an object`);
|
|
8911
|
-
}
|
|
8912
|
-
const item = s;
|
|
8913
|
-
const title = item["title"];
|
|
8914
|
-
if (typeof title !== "string" || title.trim() === "") {
|
|
8915
|
-
throw new Error(
|
|
8916
|
-
`initiativeSuggestions[${i}].title must be a non-empty string`
|
|
8917
|
-
);
|
|
8918
|
-
}
|
|
8919
|
-
const content = item["content"];
|
|
8920
|
-
if (typeof content !== "string") {
|
|
8921
|
-
throw new Error(
|
|
8922
|
-
`initiativeSuggestions[${i}].content must be a string`
|
|
8923
|
-
);
|
|
8924
|
-
}
|
|
8925
|
-
return {
|
|
8926
|
-
title: title.trim(),
|
|
8927
|
-
content,
|
|
8928
|
-
taskRefs: Array.isArray(item["taskRefs"]) ? item["taskRefs"] : void 0
|
|
8929
|
-
};
|
|
8930
|
-
}
|
|
8931
|
-
);
|
|
8932
|
-
}
|
|
8933
|
-
const result = await client.suggestTasksDedicated({
|
|
8934
|
-
suggestions,
|
|
8935
|
-
parentTaskId,
|
|
8936
|
-
initiativeSuggestions
|
|
8937
|
-
});
|
|
8938
|
-
const initiativeCount = initiativeSuggestions?.length ?? 0;
|
|
8939
|
-
const summaryParts = [
|
|
8940
|
-
`Suggested ${suggestions.length} task${suggestions.length === 1 ? "" : "s"} for human approval`,
|
|
8941
|
-
...initiativeCount > 0 ? [
|
|
8942
|
-
`and ${initiativeCount} initiative${initiativeCount === 1 ? "" : "s"}`
|
|
8943
|
-
] : []
|
|
8944
|
-
];
|
|
8945
|
-
return formatConfirmation(
|
|
8946
|
-
summaryParts.join(" "),
|
|
8947
|
-
[
|
|
8948
|
-
'Tasks created with "suggested" status \u2014 human will approve or reject each',
|
|
8949
|
-
...initiativeCount > 0 ? ["Initiative suggestions created \u2014 human will approve or reject independently"] : [],
|
|
8950
|
-
"Use deskfree_state to check for approved tasks"
|
|
8951
|
-
],
|
|
8952
|
-
result
|
|
8953
|
-
);
|
|
8954
|
-
} catch (err) {
|
|
8955
|
-
return errorResult(err);
|
|
8956
|
-
}
|
|
8957
|
-
}
|
|
9113
|
+
execute: makeSuggestTasksHandler(client)
|
|
8958
9114
|
},
|
|
8959
9115
|
{
|
|
8960
9116
|
...ORCHESTRATOR_TOOLS.CLAIM_EVALUATION,
|
|
8961
|
-
|
|
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
|
-
return {
|
|
8972
|
-
content: [
|
|
8973
|
-
{
|
|
8974
|
-
type: "text",
|
|
8975
|
-
text: JSON.stringify(
|
|
8976
|
-
{
|
|
8977
|
-
summary: `Claimed evaluation for task "${result.task.title}"`,
|
|
8978
|
-
nextActions: [
|
|
8979
|
-
"Review the task messages, current global ways of working, and initiative content (if present)",
|
|
8980
|
-
"Decide what to update: globalWoW (universal patterns), initiative (area-specific learnings), both, or neither",
|
|
8981
|
-
"Call deskfree_submit_evaluation with your analysis"
|
|
8982
|
-
],
|
|
8983
|
-
task: result.task,
|
|
8984
|
-
waysOfWorking: result.waysOfWorking,
|
|
8985
|
-
currentVersion: result.currentVersion,
|
|
8986
|
-
messages: result.messages,
|
|
8987
|
-
...result.initiative ? { initiative: result.initiative } : {}
|
|
8988
|
-
},
|
|
8989
|
-
null,
|
|
8990
|
-
2
|
|
8991
|
-
)
|
|
8992
|
-
}
|
|
8993
|
-
]
|
|
8994
|
-
};
|
|
8995
|
-
} catch (err) {
|
|
8996
|
-
return errorResult(err);
|
|
8997
|
-
}
|
|
8998
|
-
}
|
|
9117
|
+
execute: makeClaimEvaluationHandler(client)
|
|
8999
9118
|
},
|
|
9000
9119
|
{
|
|
9001
9120
|
...ORCHESTRATOR_TOOLS.SUBMIT_EVALUATION,
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
const rawGlobalWoW = params?.globalWoW;
|
|
9007
|
-
if (typeof rawGlobalWoW !== "object" || rawGlobalWoW === null) {
|
|
9008
|
-
throw new Error('Parameter "globalWoW" must be an object');
|
|
9009
|
-
}
|
|
9010
|
-
const globalWoWObj = rawGlobalWoW;
|
|
9011
|
-
if (typeof globalWoWObj["hasChanges"] !== "boolean") {
|
|
9012
|
-
throw new Error('Parameter "globalWoW.hasChanges" must be a boolean');
|
|
9013
|
-
}
|
|
9014
|
-
const globalWoW = {
|
|
9015
|
-
hasChanges: globalWoWObj["hasChanges"],
|
|
9016
|
-
updatedContent: typeof globalWoWObj["updatedContent"] === "string" ? globalWoWObj["updatedContent"] : void 0
|
|
9017
|
-
};
|
|
9018
|
-
const rawInitiative = params?.initiative;
|
|
9019
|
-
if (typeof rawInitiative !== "object" || rawInitiative === null) {
|
|
9020
|
-
throw new Error('Parameter "initiative" must be an object');
|
|
9021
|
-
}
|
|
9022
|
-
const initiativeObj = rawInitiative;
|
|
9023
|
-
if (typeof initiativeObj["hasChanges"] !== "boolean") {
|
|
9024
|
-
throw new Error('Parameter "initiative.hasChanges" must be a boolean');
|
|
9025
|
-
}
|
|
9026
|
-
const initiative = {
|
|
9027
|
-
hasChanges: initiativeObj["hasChanges"],
|
|
9028
|
-
updatedContent: typeof initiativeObj["updatedContent"] === "string" ? initiativeObj["updatedContent"] : void 0
|
|
9029
|
-
};
|
|
9030
|
-
const result = await client.submitEvaluation({
|
|
9031
|
-
taskId,
|
|
9032
|
-
reasoning,
|
|
9033
|
-
globalWoW,
|
|
9034
|
-
initiative
|
|
9035
|
-
});
|
|
9036
|
-
const parts = [];
|
|
9037
|
-
if (globalWoW.hasChanges) {
|
|
9038
|
-
parts.push(`global WoW \u2192 v${result.globalVersion}`);
|
|
9039
|
-
}
|
|
9040
|
-
if (initiative.hasChanges && result.initiativeVersion !== void 0) {
|
|
9041
|
-
parts.push(`initiative \u2192 v${result.initiativeVersion}`);
|
|
9042
|
-
}
|
|
9043
|
-
const messageContent = parts.length > 0 ? `\u{1F4DD} Updated ${parts.join(", ")}: ${reasoning}` : `\u{1F4DD} No updates to ways of working: ${reasoning}`;
|
|
9044
|
-
await client.sendMessage({ content: messageContent, taskId }).catch(() => {
|
|
9045
|
-
});
|
|
9046
|
-
const summaryParts = [];
|
|
9047
|
-
if (globalWoW.hasChanges) summaryParts.push(`global WoW v${result.globalVersion}`);
|
|
9048
|
-
if (initiative.hasChanges && result.initiativeVersion !== void 0) {
|
|
9049
|
-
summaryParts.push(`initiative v${result.initiativeVersion}`);
|
|
9050
|
-
}
|
|
9051
|
-
return formatConfirmation(
|
|
9052
|
-
summaryParts.length > 0 ? `Evaluation complete \u2014 updated ${summaryParts.join(", ")}` : "Evaluation complete \u2014 no changes needed",
|
|
9053
|
-
["Use deskfree_state to check for other pending evaluations"],
|
|
9054
|
-
result
|
|
9055
|
-
);
|
|
9056
|
-
} catch (err) {
|
|
9057
|
-
return errorResult(err);
|
|
9058
|
-
}
|
|
9059
|
-
}
|
|
9121
|
+
execute: makeSubmitEvaluationHandler(
|
|
9122
|
+
client,
|
|
9123
|
+
"Use deskfree_state to check for other pending evaluations"
|
|
9124
|
+
)
|
|
9060
9125
|
}
|
|
9061
9126
|
];
|
|
9062
9127
|
}
|