@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/README.md +25 -4
- package/dist/index.d.ts +106 -46
- package/dist/index.js +566 -323
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/deskfree/SKILL.md +409 -113
- package/skills/deskfree/references/tools.md +76 -32
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. */
|
|
@@ -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
|
-
/**
|
|
3903
|
-
async
|
|
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.
|
|
3906
|
-
|
|
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
|
|
3932
|
-
async
|
|
3933
|
-
if (!input.
|
|
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
|
-
"
|
|
3937
|
-
"
|
|
3938
|
-
"Missing required parameter:
|
|
3938
|
+
"suggestions",
|
|
3939
|
+
"suggestions array is required and cannot be empty",
|
|
3940
|
+
"Missing required parameter: suggestions."
|
|
3939
3941
|
);
|
|
3940
3942
|
}
|
|
3941
|
-
return this.
|
|
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
|
-
*
|
|
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
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
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,
|
|
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
|
-
|
|
7779
|
-
name: "
|
|
7780
|
-
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.`,
|
|
7781
7812
|
parameters: Type.Object({
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
description: "
|
|
7813
|
+
fileId: Type.String({ description: "File ID to update" }),
|
|
7814
|
+
content: Type.String({
|
|
7815
|
+
description: "Full file content (replaces previous)"
|
|
7785
7816
|
}),
|
|
7786
|
-
|
|
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.
|
|
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
|
|
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).
|
|
7861
|
+
description: "Send a message in the task thread (progress update, question, status report).",
|
|
7806
7862
|
parameters: Type.Object({
|
|
7807
|
-
content: Type.
|
|
7863
|
+
content: Type.String({
|
|
7864
|
+
description: "Message content."
|
|
7865
|
+
}),
|
|
7866
|
+
taskId: Type.Optional(
|
|
7808
7867
|
Type.String({
|
|
7809
|
-
description: "
|
|
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
|
-
|
|
7929
|
+
parentTaskId: Type.Optional(
|
|
7813
7930
|
Type.String({
|
|
7814
|
-
description: "
|
|
7931
|
+
description: "Parent task ID \u2014 set when suggesting follow-ups from within a task"
|
|
7815
7932
|
})
|
|
7816
7933
|
),
|
|
7817
|
-
|
|
7934
|
+
initiativeSuggestions: Type.Optional(
|
|
7818
7935
|
Type.Array(
|
|
7819
7936
|
Type.Object({
|
|
7820
|
-
title: Type.String({
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
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:
|
|
7950
|
+
description: 'Propose new initiatives (optional). Created with "suggested" status \u2014 human approves or rejects independently.',
|
|
7829
7951
|
minItems: 1,
|
|
7830
|
-
maxItems:
|
|
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,
|
|
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.
|
|
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
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
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
|
-
|
|
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
|
|
8425
|
-
1.
|
|
8426
|
-
2.
|
|
8427
|
-
3.
|
|
8428
|
-
4.
|
|
8429
|
-
5.
|
|
8430
|
-
6.
|
|
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
|
-
|
|
8685
|
-
|
|
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.
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
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
|
}
|