@questionbase/deskfree 0.3.0-alpha.35 → 0.3.0-alpha.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +16 -20
- package/dist/index.js +286 -132
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/deskfree/SKILL.md +115 -149
- package/skills/deskfree/references/tools.md +60 -57
package/dist/index.d.ts
CHANGED
|
@@ -418,7 +418,7 @@ interface WsNotification {
|
|
|
418
418
|
interface Task {
|
|
419
419
|
taskId: string;
|
|
420
420
|
title: string;
|
|
421
|
-
status: '
|
|
421
|
+
status: 'open' | 'done';
|
|
422
422
|
instructions?: string;
|
|
423
423
|
createdAt: string;
|
|
424
424
|
updatedAt: string;
|
|
@@ -431,7 +431,7 @@ interface Task {
|
|
|
431
431
|
interface TaskSummary {
|
|
432
432
|
taskId: string;
|
|
433
433
|
title: string;
|
|
434
|
-
status: '
|
|
434
|
+
status: 'open' | 'done';
|
|
435
435
|
instructions: string | null;
|
|
436
436
|
fileId: string | null;
|
|
437
437
|
fileName: string | null;
|
|
@@ -468,13 +468,20 @@ interface TaskWithContext extends Task {
|
|
|
468
468
|
}
|
|
469
469
|
interface CompleteTaskInput {
|
|
470
470
|
taskId: string;
|
|
471
|
-
|
|
472
|
-
|
|
471
|
+
learnings?: {
|
|
472
|
+
reasoning: string;
|
|
473
|
+
globalWoW?: string;
|
|
474
|
+
initiativeContent?: string;
|
|
475
|
+
};
|
|
476
|
+
followUps?: Array<{
|
|
477
|
+
title: string;
|
|
478
|
+
instructions?: string;
|
|
479
|
+
}>;
|
|
473
480
|
}
|
|
474
481
|
interface WorkspaceStateTask {
|
|
475
482
|
taskId: string;
|
|
476
483
|
title: string;
|
|
477
|
-
status: '
|
|
484
|
+
status: 'open' | 'done';
|
|
478
485
|
instructions?: string | null;
|
|
479
486
|
fileId?: string | null;
|
|
480
487
|
fileName?: string | null;
|
|
@@ -564,6 +571,7 @@ declare class DeskFreeClient {
|
|
|
564
571
|
userId?: string;
|
|
565
572
|
content: string;
|
|
566
573
|
taskId?: string;
|
|
574
|
+
messageType?: 'ask' | 'notify';
|
|
567
575
|
attachments?: Array<{
|
|
568
576
|
s3Key: string;
|
|
569
577
|
name: string;
|
|
@@ -659,10 +667,8 @@ declare class DeskFreeClient {
|
|
|
659
667
|
}): Promise<{
|
|
660
668
|
success: boolean;
|
|
661
669
|
}>;
|
|
662
|
-
/** Complete a task
|
|
663
|
-
completeTask(input: CompleteTaskInput): Promise<Task
|
|
664
|
-
outcome: 'done' | 'blocked';
|
|
665
|
-
}>;
|
|
670
|
+
/** Complete a task. Marks it as done. */
|
|
671
|
+
completeTask(input: CompleteTaskInput): Promise<Task>;
|
|
666
672
|
/** Reopen a completed/human task back to bot status for further work. */
|
|
667
673
|
reopenTask(input: {
|
|
668
674
|
taskId: string;
|
|
@@ -701,20 +707,10 @@ declare class DeskFreeClient {
|
|
|
701
707
|
content: string;
|
|
702
708
|
};
|
|
703
709
|
context?: string;
|
|
710
|
+
taskId?: string;
|
|
704
711
|
}): Promise<{
|
|
705
712
|
messageId: string;
|
|
706
713
|
}>;
|
|
707
|
-
/** Update ways of working and/or initiative content based on learnings. */
|
|
708
|
-
updateKnowledge(input: {
|
|
709
|
-
globalWoW?: string;
|
|
710
|
-
initiativeId?: string;
|
|
711
|
-
initiativeContent?: string;
|
|
712
|
-
reasoning: string;
|
|
713
|
-
}): Promise<{
|
|
714
|
-
success: boolean;
|
|
715
|
-
globalVersion?: number;
|
|
716
|
-
initiativeVersion?: number;
|
|
717
|
-
}>;
|
|
718
714
|
/**
|
|
719
715
|
* Lightweight health check that verifies connectivity and authentication.
|
|
720
716
|
*
|
package/dist/index.js
CHANGED
|
@@ -3932,7 +3932,7 @@ var DeskFreeClient = class {
|
|
|
3932
3932
|
this.requireNonEmpty(input.model, "model");
|
|
3933
3933
|
return this.request("POST", "tasks.reportUsage", input);
|
|
3934
3934
|
}
|
|
3935
|
-
/** Complete a task
|
|
3935
|
+
/** Complete a task. Marks it as done. */
|
|
3936
3936
|
async completeTask(input) {
|
|
3937
3937
|
this.requireNonEmpty(input.taskId, "taskId");
|
|
3938
3938
|
return this.request("POST", "tasks.complete", input);
|
|
@@ -3966,11 +3966,6 @@ var DeskFreeClient = class {
|
|
|
3966
3966
|
}
|
|
3967
3967
|
return this.request("POST", "tasks.propose", input);
|
|
3968
3968
|
}
|
|
3969
|
-
/** Update ways of working and/or initiative content based on learnings. */
|
|
3970
|
-
async updateKnowledge(input) {
|
|
3971
|
-
this.requireNonEmpty(input.reasoning, "reasoning");
|
|
3972
|
-
return this.request("POST", "waysOfWorking.update", input);
|
|
3973
|
-
}
|
|
3974
3969
|
/**
|
|
3975
3970
|
* Lightweight health check that verifies connectivity and authentication.
|
|
3976
3971
|
*
|
|
@@ -4492,10 +4487,10 @@ function buildBodyForAgent(task, content, recentMessages) {
|
|
|
4492
4487
|
return prefix + "\n\n" + content;
|
|
4493
4488
|
}
|
|
4494
4489
|
function resolveTaskRouting(task, hasAttachments) {
|
|
4495
|
-
if (task.status === "
|
|
4490
|
+
if (task.status === "open") {
|
|
4496
4491
|
return { target: "runner" };
|
|
4497
4492
|
}
|
|
4498
|
-
if (hasAttachments &&
|
|
4493
|
+
if (hasAttachments && task.status === "done") {
|
|
4499
4494
|
return { target: "auto-reopen" };
|
|
4500
4495
|
}
|
|
4501
4496
|
return { target: "orchestrator" };
|
|
@@ -7982,7 +7977,7 @@ var ORCHESTRATOR_TOOLS = {
|
|
|
7982
7977
|
},
|
|
7983
7978
|
REOPEN_TASK: {
|
|
7984
7979
|
name: "deskfree_reopen_task",
|
|
7985
|
-
description: "Reopen a
|
|
7980
|
+
description: "Reopen a task (from review or done) back to pending. Use when more work is needed on a task. Works on both completed and in-review tasks.",
|
|
7986
7981
|
parameters: Type.Object({
|
|
7987
7982
|
taskId: Type.String({ description: "Task UUID to reopen" }),
|
|
7988
7983
|
reason: Type.Optional(
|
|
@@ -7992,30 +7987,6 @@ var ORCHESTRATOR_TOOLS = {
|
|
|
7992
7987
|
)
|
|
7993
7988
|
})
|
|
7994
7989
|
},
|
|
7995
|
-
UPDATE_KNOWLEDGE: {
|
|
7996
|
-
name: "deskfree_update_knowledge",
|
|
7997
|
-
description: "Update ways of working and/or initiative content based on learnings from completed tasks.",
|
|
7998
|
-
parameters: Type.Object({
|
|
7999
|
-
globalWoW: Type.Optional(
|
|
8000
|
-
Type.String({
|
|
8001
|
-
description: "Full updated global ways-of-working markdown content"
|
|
8002
|
-
})
|
|
8003
|
-
),
|
|
8004
|
-
initiativeId: Type.Optional(
|
|
8005
|
-
Type.String({
|
|
8006
|
-
description: "Initiative ID to update"
|
|
8007
|
-
})
|
|
8008
|
-
),
|
|
8009
|
-
initiativeContent: Type.Optional(
|
|
8010
|
-
Type.String({
|
|
8011
|
-
description: "Full updated initiative content markdown (required if initiativeId provided)"
|
|
8012
|
-
})
|
|
8013
|
-
),
|
|
8014
|
-
reasoning: Type.String({
|
|
8015
|
-
description: "Explanation of why these updates were made and what was learned"
|
|
8016
|
-
})
|
|
8017
|
-
})
|
|
8018
|
-
},
|
|
8019
7990
|
SEND_MESSAGE: {
|
|
8020
7991
|
name: "deskfree_send_message",
|
|
8021
7992
|
description: "Send a message to the human. Keep it short \u2014 1-3 sentences. No walls of text.",
|
|
@@ -8124,6 +8095,11 @@ var ORCHESTRATOR_TOOLS = {
|
|
|
8124
8095
|
minItems: 1,
|
|
8125
8096
|
maxItems: 20
|
|
8126
8097
|
}
|
|
8098
|
+
),
|
|
8099
|
+
taskId: Type.Optional(
|
|
8100
|
+
Type.String({
|
|
8101
|
+
description: "Task ID to thread this proposal into (for follow-up proposals from within a task)"
|
|
8102
|
+
})
|
|
8127
8103
|
)
|
|
8128
8104
|
})
|
|
8129
8105
|
}
|
|
@@ -8146,36 +8122,63 @@ var SHARED_TOOLS = {
|
|
|
8146
8122
|
},
|
|
8147
8123
|
COMPLETE_TASK: {
|
|
8148
8124
|
name: "deskfree_complete_task",
|
|
8149
|
-
description: "
|
|
8125
|
+
description: "Mark a task as done. Only call when truly finished and human confirmed. Pass learnings to update knowledge atomically with completion. Pass followUps to propose next tasks.",
|
|
8150
8126
|
parameters: Type.Object({
|
|
8151
8127
|
taskId: Type.String({ description: "Task UUID" }),
|
|
8152
|
-
outcome: Type.Union(
|
|
8153
|
-
[
|
|
8154
|
-
Type.Literal("review"),
|
|
8155
|
-
Type.Literal("done"),
|
|
8156
|
-
Type.Literal("blocked"),
|
|
8157
|
-
Type.Literal("cancelled")
|
|
8158
|
-
],
|
|
8159
|
-
{
|
|
8160
|
-
description: '"review" = ready for human review, "done" = work complete, "blocked" = need human input, "cancelled" = task cancelled'
|
|
8161
|
-
}
|
|
8162
|
-
),
|
|
8163
|
-
summary: Type.Optional(
|
|
8164
|
-
Type.String({
|
|
8165
|
-
description: "1-2 sentence summary of what was done and the outcome."
|
|
8166
|
-
})
|
|
8167
|
-
),
|
|
8168
8128
|
learnings: Type.Optional(
|
|
8169
|
-
Type.
|
|
8170
|
-
|
|
8171
|
-
|
|
8129
|
+
Type.Object(
|
|
8130
|
+
{
|
|
8131
|
+
reasoning: Type.String({
|
|
8132
|
+
description: "Why these updates matter \u2014 what was learned from this task"
|
|
8133
|
+
}),
|
|
8134
|
+
globalWoW: Type.Optional(
|
|
8135
|
+
Type.String({
|
|
8136
|
+
description: "Full updated global Ways of Working markdown content (full replacement, not diff)"
|
|
8137
|
+
})
|
|
8138
|
+
),
|
|
8139
|
+
initiativeId: Type.Optional(
|
|
8140
|
+
Type.String({
|
|
8141
|
+
description: "Initiative ID to update"
|
|
8142
|
+
})
|
|
8143
|
+
),
|
|
8144
|
+
initiativeContent: Type.Optional(
|
|
8145
|
+
Type.String({
|
|
8146
|
+
description: "Full updated initiative content markdown (full replacement, not diff)"
|
|
8147
|
+
})
|
|
8148
|
+
)
|
|
8149
|
+
},
|
|
8150
|
+
{
|
|
8151
|
+
description: "Knowledge updates to apply atomically with task completion. Include only if there are genuine learnings to record."
|
|
8152
|
+
}
|
|
8153
|
+
)
|
|
8154
|
+
),
|
|
8155
|
+
followUps: Type.Optional(
|
|
8156
|
+
Type.Array(
|
|
8157
|
+
Type.Object({
|
|
8158
|
+
title: Type.String({
|
|
8159
|
+
description: "Follow-up task title (max 200 chars)"
|
|
8160
|
+
}),
|
|
8161
|
+
instructions: Type.Optional(
|
|
8162
|
+
Type.String({
|
|
8163
|
+
description: "Instructions for the follow-up task"
|
|
8164
|
+
})
|
|
8165
|
+
)
|
|
8166
|
+
}),
|
|
8167
|
+
{
|
|
8168
|
+
description: "Follow-up tasks to propose for human approval (max 10). Use when the work revealed clear next steps.",
|
|
8169
|
+
maxItems: 10
|
|
8170
|
+
}
|
|
8171
|
+
)
|
|
8172
8172
|
)
|
|
8173
8173
|
})
|
|
8174
8174
|
},
|
|
8175
8175
|
SEND_MESSAGE: {
|
|
8176
8176
|
name: "deskfree_send_message",
|
|
8177
|
-
description: "Send a message in the task thread. Keep it short \u2014 1-3 sentences.
|
|
8177
|
+
description: "Send a message in the task thread. Keep it short \u2014 1-3 sentences.",
|
|
8178
8178
|
parameters: Type.Object({
|
|
8179
|
+
type: Type.Union([Type.Literal("notify"), Type.Literal("ask")], {
|
|
8180
|
+
description: "notify = progress update (quiet, collapsible). ask = needs human attention (surfaces to main thread). Terminate after sending an ask."
|
|
8181
|
+
}),
|
|
8179
8182
|
content: Type.String({
|
|
8180
8183
|
description: "Message content."
|
|
8181
8184
|
}),
|
|
@@ -8280,6 +8283,11 @@ var SHARED_TOOLS = {
|
|
|
8280
8283
|
minItems: 1,
|
|
8281
8284
|
maxItems: 20
|
|
8282
8285
|
}
|
|
8286
|
+
),
|
|
8287
|
+
taskId: Type.Optional(
|
|
8288
|
+
Type.String({
|
|
8289
|
+
description: "Task ID to thread this proposal into (for follow-up proposals from within a task)"
|
|
8290
|
+
})
|
|
8283
8291
|
)
|
|
8284
8292
|
})
|
|
8285
8293
|
}
|
|
@@ -8300,7 +8308,16 @@ var WORKER_TOOLS = {
|
|
|
8300
8308
|
UPDATE_FILE: SHARED_TOOLS.UPDATE_FILE,
|
|
8301
8309
|
COMPLETE_TASK: SHARED_TOOLS.COMPLETE_TASK,
|
|
8302
8310
|
SEND_MESSAGE: SHARED_TOOLS.SEND_MESSAGE,
|
|
8303
|
-
PROPOSE: SHARED_TOOLS.PROPOSE
|
|
8311
|
+
PROPOSE: SHARED_TOOLS.PROPOSE,
|
|
8312
|
+
READ_SKILL: {
|
|
8313
|
+
name: "deskfree_read_skill_section",
|
|
8314
|
+
description: "Load full instructions for a skill. Use after deskfree_start_task when you need the complete skill guide beyond the critical section summary.",
|
|
8315
|
+
parameters: Type.Object({
|
|
8316
|
+
skillId: Type.String({
|
|
8317
|
+
description: "Skill ID to load instructions for"
|
|
8318
|
+
})
|
|
8319
|
+
})
|
|
8320
|
+
}
|
|
8304
8321
|
};
|
|
8305
8322
|
var CHANNEL_META = {
|
|
8306
8323
|
name: "DeskFree",
|
|
@@ -8877,8 +8894,8 @@ You are the orchestrator. Your job: turn human intent into approved tasks, then
|
|
|
8877
8894
|
|
|
8878
8895
|
**Match the human's energy.** Short message \u2192 short reply. Casual tone \u2192 casual response. Don't over-explain, don't lecture, don't pad responses.
|
|
8879
8896
|
|
|
8880
|
-
You do NOT claim tasks or do work directly.
|
|
8881
|
-
- When a human writes in a task thread,
|
|
8897
|
+
You do NOT claim tasks or do work directly \u2014 you have no access to deskfree_start_task. Spawn a sub-agent for each approved task and pass it the taskId.
|
|
8898
|
+
- When a human writes in a task thread, decide: does it need bot action? If yes \u2192 reopen and spawn sub-agent. If it's confirmation ("looks good") \u2192 complete the task. If deferred ("I'll check later") \u2192 leave it.
|
|
8882
8899
|
- Write task instructions as rich markdown (bold, lists, inline code \u2014 no # headers). Brief a contractor who has never seen the codebase.
|
|
8883
8900
|
- Estimate token cost per task \u2014 consider files to read, reasoning, output.
|
|
8884
8901
|
- One initiative per proposal \u2014 make multiple calls for multiple initiatives.
|
|
@@ -8889,10 +8906,12 @@ You are a worker sub-agent. Call \`deskfree_start_task\` with your taskId to cla
|
|
|
8889
8906
|
Tools: deskfree_start_task, deskfree_update_file, deskfree_complete_task, deskfree_send_message, deskfree_propose.
|
|
8890
8907
|
- Claim your task first with deskfree_start_task \u2014 this loads instructions, messages, and file context.
|
|
8891
8908
|
- Save work to linked files with deskfree_update_file (incrementally).
|
|
8892
|
-
-
|
|
8893
|
-
-
|
|
8894
|
-
-
|
|
8895
|
-
-
|
|
8909
|
+
- Use deskfree_send_message with type "notify" for progress updates \u2014 what you're doing, what you found. Keep it brief.
|
|
8910
|
+
- Use deskfree_send_message with type "ask" when you need human input OR when your work is done for review. This surfaces to the main thread. Terminate after sending an ask.
|
|
8911
|
+
- When completing: pass "learnings" to deskfree_complete_task if you have knowledge updates (WoW or initiative content). Pass "followUps" if the work revealed clear next steps. Knowledge updates and proposals happen atomically with completion \u2014 no separate tool calls needed.
|
|
8912
|
+
- Only complete when the human has confirmed or no review is needed.
|
|
8913
|
+
- Write like a senior colleague giving a status update \u2014 not a report. 1-3 sentences for messages.
|
|
8914
|
+
- On 409 or 404 errors: STOP. Do not retry the same taskId. Call deskfree_state to find available tasks.`;
|
|
8896
8915
|
function getDeskFreeContext(sessionKey) {
|
|
8897
8916
|
const isWorker = sessionKey && (sessionKey.includes(":sub:") || sessionKey.includes(":spawn:") || sessionKey.includes(":run:"));
|
|
8898
8917
|
const directive = isWorker ? DESKFREE_WORKER_DIRECTIVE : DESKFREE_AGENT_DIRECTIVE;
|
|
@@ -8901,6 +8920,91 @@ function getDeskFreeContext(sessionKey) {
|
|
|
8901
8920
|
<!-- deskfree-plugin:${PLUGIN_VERSION} -->`;
|
|
8902
8921
|
}
|
|
8903
8922
|
|
|
8923
|
+
// src/skill-scan-hook.ts
|
|
8924
|
+
var SCANNABLE_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".sh", ".py", ".js", ".ts"]);
|
|
8925
|
+
var SKILLS_DIR_PATTERN = /(?:^|\/|\\)skills\/[^/\\]+\//;
|
|
8926
|
+
function isSkillFile(filePath) {
|
|
8927
|
+
if (!filePath || typeof filePath !== "string") return false;
|
|
8928
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
8929
|
+
if (!SKILLS_DIR_PATTERN.test(normalized)) return false;
|
|
8930
|
+
const lastDot = normalized.lastIndexOf(".");
|
|
8931
|
+
if (lastDot === -1) return false;
|
|
8932
|
+
const ext = normalized.slice(lastDot).toLowerCase();
|
|
8933
|
+
return SCANNABLE_EXTENSIONS.has(ext);
|
|
8934
|
+
}
|
|
8935
|
+
var WRITE_TOOL_NAMES = /* @__PURE__ */ new Set(["write", "Write"]);
|
|
8936
|
+
var EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit", "Edit"]);
|
|
8937
|
+
function extractPath(params) {
|
|
8938
|
+
const p = params["path"] ?? params["file_path"];
|
|
8939
|
+
return typeof p === "string" && p.length > 0 ? p : null;
|
|
8940
|
+
}
|
|
8941
|
+
function extractContent(toolName, params) {
|
|
8942
|
+
if (WRITE_TOOL_NAMES.has(toolName)) {
|
|
8943
|
+
const c = params["content"];
|
|
8944
|
+
return typeof c === "string" ? c : null;
|
|
8945
|
+
}
|
|
8946
|
+
if (EDIT_TOOL_NAMES.has(toolName)) {
|
|
8947
|
+
const c = params["newText"] ?? params["new_string"];
|
|
8948
|
+
return typeof c === "string" ? c : null;
|
|
8949
|
+
}
|
|
8950
|
+
return null;
|
|
8951
|
+
}
|
|
8952
|
+
function formatFindings(result) {
|
|
8953
|
+
return result.findings.map(
|
|
8954
|
+
(f) => ` [${f.severity.toUpperCase()}] ${f.description}` + (f.line != null ? ` (line ${f.line})` : "")
|
|
8955
|
+
).join("\n");
|
|
8956
|
+
}
|
|
8957
|
+
function createSkillScanHook(scanner, log) {
|
|
8958
|
+
return (event, _ctx) => {
|
|
8959
|
+
const toolName = event["toolName"];
|
|
8960
|
+
if (typeof toolName !== "string") return;
|
|
8961
|
+
if (!WRITE_TOOL_NAMES.has(toolName) && !EDIT_TOOL_NAMES.has(toolName)) {
|
|
8962
|
+
return;
|
|
8963
|
+
}
|
|
8964
|
+
const params = event["params"];
|
|
8965
|
+
if (!params || typeof params !== "object") return;
|
|
8966
|
+
const p = params;
|
|
8967
|
+
const filePath = extractPath(p);
|
|
8968
|
+
if (!filePath) return;
|
|
8969
|
+
if (!isSkillFile(filePath)) return;
|
|
8970
|
+
const content = extractContent(toolName, p);
|
|
8971
|
+
if (!content || content.length === 0) return;
|
|
8972
|
+
const filename = filePath.split("/").pop() ?? "unknown";
|
|
8973
|
+
const inputs = [{ filename, content }];
|
|
8974
|
+
let result;
|
|
8975
|
+
try {
|
|
8976
|
+
result = scanner(inputs);
|
|
8977
|
+
} catch (err) {
|
|
8978
|
+
log.warn(
|
|
8979
|
+
`[skill-scan] Scanner error for ${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
8980
|
+
);
|
|
8981
|
+
return;
|
|
8982
|
+
}
|
|
8983
|
+
log.info(
|
|
8984
|
+
`[skill-scan] ${filePath}: verdict=${result.verdict} score=${result.score} findings=${result.findings.length}`
|
|
8985
|
+
);
|
|
8986
|
+
if (result.verdict === "dangerous") {
|
|
8987
|
+
const findings = formatFindings(result);
|
|
8988
|
+
log.warn(`[skill-scan] BLOCKED write to ${filePath}:
|
|
8989
|
+
${findings}`);
|
|
8990
|
+
return {
|
|
8991
|
+
block: true,
|
|
8992
|
+
blockReason: `\u{1F6AB} Security scan BLOCKED write to "${filename}" \u2014 dangerous (score: ${result.score}/100)
|
|
8993
|
+
|
|
8994
|
+
Findings:
|
|
8995
|
+
${findings}
|
|
8996
|
+
|
|
8997
|
+
Remove the dangerous patterns and retry.`
|
|
8998
|
+
};
|
|
8999
|
+
}
|
|
9000
|
+
if (result.verdict === "suspicious") {
|
|
9001
|
+
const findings = formatFindings(result);
|
|
9002
|
+
log.warn(`[skill-scan] Suspicious write to ${filePath}:
|
|
9003
|
+
${findings}`);
|
|
9004
|
+
}
|
|
9005
|
+
};
|
|
9006
|
+
}
|
|
9007
|
+
|
|
8904
9008
|
// src/tools.ts
|
|
8905
9009
|
function resolveAccountFromConfig(api) {
|
|
8906
9010
|
const cfg = api.runtime.config.loadConfig();
|
|
@@ -9016,9 +9120,9 @@ function suggestionForErrorType(type, statusCode) {
|
|
|
9016
9120
|
if (statusCode === 429)
|
|
9017
9121
|
return "Too many requests. Wait a moment before trying again.";
|
|
9018
9122
|
if (statusCode === 409)
|
|
9019
|
-
return "
|
|
9123
|
+
return "STOP \u2014 this task is already claimed or completed. Do NOT retry. Call deskfree_state to find available tasks.";
|
|
9020
9124
|
if (statusCode === 404)
|
|
9021
|
-
return "
|
|
9125
|
+
return "STOP \u2014 task not found or not in claimable state. Do NOT retry. Call deskfree_state to find available tasks.";
|
|
9022
9126
|
return "Check your input parameters and try again.";
|
|
9023
9127
|
default:
|
|
9024
9128
|
return "";
|
|
@@ -9188,34 +9292,46 @@ function makeCompleteTaskHandler(client) {
|
|
|
9188
9292
|
return async (_id, params) => {
|
|
9189
9293
|
try {
|
|
9190
9294
|
const taskId = validateStringParam(params, "taskId", true);
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
"
|
|
9194
|
-
["
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9295
|
+
let learnings;
|
|
9296
|
+
if (params["learnings"] && typeof params["learnings"] === "object") {
|
|
9297
|
+
const raw = params["learnings"];
|
|
9298
|
+
const reasoning = typeof raw["reasoning"] === "string" ? raw["reasoning"] : "";
|
|
9299
|
+
if (reasoning.trim()) {
|
|
9300
|
+
learnings = {
|
|
9301
|
+
reasoning,
|
|
9302
|
+
globalWoW: typeof raw["globalWoW"] === "string" && raw["globalWoW"].trim() ? raw["globalWoW"] : void 0,
|
|
9303
|
+
initiativeContent: typeof raw["initiativeContent"] === "string" && raw["initiativeContent"].trim() ? raw["initiativeContent"] : void 0
|
|
9304
|
+
};
|
|
9305
|
+
}
|
|
9306
|
+
}
|
|
9307
|
+
let followUps;
|
|
9308
|
+
if (Array.isArray(params["followUps"]) && params["followUps"].length > 0) {
|
|
9309
|
+
const parsed = params["followUps"].filter((item) => typeof item === "object" && item !== null).map((item) => {
|
|
9310
|
+
const obj = item;
|
|
9311
|
+
const title = typeof obj["title"] === "string" ? obj["title"].trim() : "";
|
|
9312
|
+
const instructions = typeof obj["instructions"] === "string" ? obj["instructions"] : void 0;
|
|
9313
|
+
return { title, instructions };
|
|
9314
|
+
}).filter((item) => item.title.length > 0);
|
|
9315
|
+
if (parsed.length > 0) followUps = parsed;
|
|
9316
|
+
}
|
|
9199
9317
|
const result = await client.completeTask({
|
|
9200
9318
|
taskId,
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
...evaluation ? { evaluation } : {}
|
|
9319
|
+
learnings,
|
|
9320
|
+
followUps
|
|
9204
9321
|
});
|
|
9205
9322
|
setActiveTaskId(null);
|
|
9206
|
-
const
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
content
|
|
9210
|
-
|
|
9211
|
-
|
|
9323
|
+
const actions = ["Use deskfree_state to check for other tasks"];
|
|
9324
|
+
if (learnings?.globalWoW) actions.unshift("Ways of Working updated");
|
|
9325
|
+
if (learnings?.initiativeContent)
|
|
9326
|
+
actions.unshift("Initiative content updated");
|
|
9327
|
+
if (followUps)
|
|
9328
|
+
actions.unshift(
|
|
9329
|
+
`${followUps.length} follow-up task(s) proposed for human approval`
|
|
9330
|
+
);
|
|
9212
9331
|
return formatTaskResponse(
|
|
9213
9332
|
result,
|
|
9214
|
-
`Task "${result.title}" marked as
|
|
9215
|
-
|
|
9216
|
-
outcome === "done" ? "Human will review your work summary" : "Human will review the blocker and provide guidance",
|
|
9217
|
-
"Use deskfree_state to check for other tasks"
|
|
9218
|
-
]
|
|
9333
|
+
`Task "${result.title}" marked as done \u2705`,
|
|
9334
|
+
actions
|
|
9219
9335
|
);
|
|
9220
9336
|
} catch (err) {
|
|
9221
9337
|
return errorResult(err);
|
|
@@ -9241,13 +9357,16 @@ function makeSendMessageHandler(client) {
|
|
|
9241
9357
|
try {
|
|
9242
9358
|
const content = validateStringParam(params, "content", true);
|
|
9243
9359
|
const taskId = validateStringParam(params, "taskId", false);
|
|
9244
|
-
|
|
9360
|
+
const messageType = validateEnumParam(
|
|
9361
|
+
params,
|
|
9362
|
+
"type",
|
|
9363
|
+
["notify", "ask"],
|
|
9364
|
+
true
|
|
9365
|
+
);
|
|
9366
|
+
await client.sendMessage({ content, taskId, messageType });
|
|
9245
9367
|
return formatConfirmation(
|
|
9246
|
-
`Message sent${taskId ? ` to task ${taskId}` : ""}`,
|
|
9247
|
-
[
|
|
9248
|
-
"Message delivered to the human",
|
|
9249
|
-
taskId ? "Continue working on the task or wait for response" : "Check for response with task messages"
|
|
9250
|
-
]
|
|
9368
|
+
`Message sent (${messageType})${taskId ? ` to task ${taskId}` : ""}`,
|
|
9369
|
+
messageType === "ask" ? ["Message surfaced to human \u2014 terminate and wait for response"] : ["Progress update sent \u2014 continue working"]
|
|
9251
9370
|
);
|
|
9252
9371
|
} catch (err) {
|
|
9253
9372
|
return errorResult(err);
|
|
@@ -9260,6 +9379,7 @@ function makeProposeHandler(client) {
|
|
|
9260
9379
|
const tasks = parseProposeTasks(params?.tasks);
|
|
9261
9380
|
const context = validateStringParam(params, "context", false);
|
|
9262
9381
|
const initiative = parseInitiative(params?.initiative);
|
|
9382
|
+
const taskId = validateStringParam(params, "taskId", false);
|
|
9263
9383
|
const result = await client.proposePlan({
|
|
9264
9384
|
tasks: tasks.map((t) => ({
|
|
9265
9385
|
title: t.title,
|
|
@@ -9270,7 +9390,8 @@ function makeProposeHandler(client) {
|
|
|
9270
9390
|
scheduledFor: t.scheduledFor
|
|
9271
9391
|
})),
|
|
9272
9392
|
initiative,
|
|
9273
|
-
context: context ?? void 0
|
|
9393
|
+
context: context ?? void 0,
|
|
9394
|
+
taskId: taskId ?? void 0
|
|
9274
9395
|
});
|
|
9275
9396
|
return formatConfirmation(
|
|
9276
9397
|
`Proposed ${tasks.length} task${tasks.length === 1 ? "" : "s"} for human approval`,
|
|
@@ -9343,41 +9464,6 @@ function createOrchestratorTools(api) {
|
|
|
9343
9464
|
...ORCHESTRATOR_TOOLS.REOPEN_TASK,
|
|
9344
9465
|
execute: makeReopenTaskHandler(client)
|
|
9345
9466
|
},
|
|
9346
|
-
{
|
|
9347
|
-
...ORCHESTRATOR_TOOLS.UPDATE_KNOWLEDGE,
|
|
9348
|
-
async execute(_id, params) {
|
|
9349
|
-
try {
|
|
9350
|
-
const globalWoW = validateStringParam(params, "globalWoW", false);
|
|
9351
|
-
const initiativeId = validateStringParam(
|
|
9352
|
-
params,
|
|
9353
|
-
"initiativeId",
|
|
9354
|
-
false
|
|
9355
|
-
);
|
|
9356
|
-
const initiativeContent = validateStringParam(
|
|
9357
|
-
params,
|
|
9358
|
-
"initiativeContent",
|
|
9359
|
-
false
|
|
9360
|
-
);
|
|
9361
|
-
const reasoning = validateStringParam(params, "reasoning", true);
|
|
9362
|
-
const result = await client.updateKnowledge({
|
|
9363
|
-
globalWoW,
|
|
9364
|
-
initiativeId,
|
|
9365
|
-
initiativeContent,
|
|
9366
|
-
reasoning
|
|
9367
|
-
});
|
|
9368
|
-
return {
|
|
9369
|
-
content: [
|
|
9370
|
-
{
|
|
9371
|
-
type: "text",
|
|
9372
|
-
text: `Knowledge updated successfully: ${JSON.stringify(result, null, 2)}`
|
|
9373
|
-
}
|
|
9374
|
-
]
|
|
9375
|
-
};
|
|
9376
|
-
} catch (err) {
|
|
9377
|
-
return errorResult(err);
|
|
9378
|
-
}
|
|
9379
|
-
}
|
|
9380
|
-
},
|
|
9381
9467
|
{
|
|
9382
9468
|
...ORCHESTRATOR_TOOLS.SEND_MESSAGE,
|
|
9383
9469
|
execute: makeSendMessageHandler(client)
|
|
@@ -9392,6 +9478,7 @@ function createWorkerTools(api) {
|
|
|
9392
9478
|
const account = resolveAccountFromConfig(api);
|
|
9393
9479
|
if (!account) return null;
|
|
9394
9480
|
const client = new DeskFreeClient(account.botToken, account.apiUrl);
|
|
9481
|
+
const cachedSkillContext = /* @__PURE__ */ new Map();
|
|
9395
9482
|
return [
|
|
9396
9483
|
{
|
|
9397
9484
|
...WORKER_TOOLS.START_TASK,
|
|
@@ -9402,19 +9489,24 @@ function createWorkerTools(api) {
|
|
|
9402
9489
|
const result = await client.claimTask({ taskId, runnerId });
|
|
9403
9490
|
setActiveTaskId(taskId);
|
|
9404
9491
|
let skillInstructions = "";
|
|
9492
|
+
cachedSkillContext.clear();
|
|
9405
9493
|
if (result.skillContext?.length) {
|
|
9494
|
+
for (const s of result.skillContext) {
|
|
9495
|
+
cachedSkillContext.set(s.skillId, {
|
|
9496
|
+
displayName: s.displayName,
|
|
9497
|
+
instructions: s.instructions
|
|
9498
|
+
});
|
|
9499
|
+
}
|
|
9406
9500
|
skillInstructions = result.skillContext.map(
|
|
9407
9501
|
(s) => `
|
|
9408
|
-
\u26A0\uFE0F SKILL: ${s.displayName}
|
|
9409
|
-
${s.criticalSection}
|
|
9410
|
-
|
|
9411
|
-
${s.instructions}`
|
|
9502
|
+
\u26A0\uFE0F SKILL: ${s.displayName} (ID: ${s.skillId})
|
|
9503
|
+
${s.criticalSection}`
|
|
9412
9504
|
).join("\n\n---\n");
|
|
9413
9505
|
}
|
|
9414
9506
|
const trimmedTask = trimTaskContext(result);
|
|
9415
9507
|
const taskJson = JSON.stringify(
|
|
9416
9508
|
{
|
|
9417
|
-
summary: `Claimed task "${result.title}" \u2014 full context loaded${result.skillContext?.length ? ` (${result.skillContext.length} skill${result.skillContext.length > 1 ? "s" : ""}
|
|
9509
|
+
summary: `Claimed task "${result.title}" \u2014 full context loaded${result.skillContext?.length ? ` (${result.skillContext.length} skill${result.skillContext.length > 1 ? "s" : ""} loaded \u2014 use deskfree_read_skill_section for full details)` : ""}`,
|
|
9418
9510
|
mode: trimmedTask.mode ?? "work",
|
|
9419
9511
|
nextActions: [
|
|
9420
9512
|
"Read the instructions and message history carefully",
|
|
@@ -9463,6 +9555,37 @@ ${s.instructions}`
|
|
|
9463
9555
|
{
|
|
9464
9556
|
...WORKER_TOOLS.PROPOSE,
|
|
9465
9557
|
execute: makeProposeHandler(client)
|
|
9558
|
+
},
|
|
9559
|
+
{
|
|
9560
|
+
...WORKER_TOOLS.READ_SKILL,
|
|
9561
|
+
async execute(_id, params) {
|
|
9562
|
+
try {
|
|
9563
|
+
const skillId = validateStringParam(params, "skillId", true);
|
|
9564
|
+
const cached = cachedSkillContext.get(skillId);
|
|
9565
|
+
if (!cached) {
|
|
9566
|
+
return {
|
|
9567
|
+
content: [
|
|
9568
|
+
{
|
|
9569
|
+
type: "text",
|
|
9570
|
+
text: `Skill ${skillId} not found in current task context. Available: ${[...cachedSkillContext.keys()].join(", ") || "none"}`
|
|
9571
|
+
}
|
|
9572
|
+
]
|
|
9573
|
+
};
|
|
9574
|
+
}
|
|
9575
|
+
return {
|
|
9576
|
+
content: [
|
|
9577
|
+
{
|
|
9578
|
+
type: "text",
|
|
9579
|
+
text: `## ${cached.displayName} \u2014 Full Instructions
|
|
9580
|
+
|
|
9581
|
+
${cached.instructions}`
|
|
9582
|
+
}
|
|
9583
|
+
]
|
|
9584
|
+
};
|
|
9585
|
+
} catch (err) {
|
|
9586
|
+
return errorResult(err);
|
|
9587
|
+
}
|
|
9588
|
+
}
|
|
9466
9589
|
}
|
|
9467
9590
|
];
|
|
9468
9591
|
}
|
|
@@ -9574,6 +9697,35 @@ var OfflineQueue = class {
|
|
|
9574
9697
|
};
|
|
9575
9698
|
|
|
9576
9699
|
// src/index.ts
|
|
9700
|
+
function createLazyScanner(log) {
|
|
9701
|
+
let resolved = false;
|
|
9702
|
+
let scanFn = null;
|
|
9703
|
+
const scannerPath = ["..", "..", "backend", "src", "util", "util.skillScanner"].join("/");
|
|
9704
|
+
import(
|
|
9705
|
+
/* @vite-ignore */
|
|
9706
|
+
scannerPath
|
|
9707
|
+
).then((mod) => {
|
|
9708
|
+
if (typeof mod.scanSkill === "function") {
|
|
9709
|
+
scanFn = mod.scanSkill;
|
|
9710
|
+
log.info("[deskfree] Skill security scanner loaded");
|
|
9711
|
+
}
|
|
9712
|
+
resolved = true;
|
|
9713
|
+
}).catch(() => {
|
|
9714
|
+
resolved = true;
|
|
9715
|
+
log.warn("[deskfree] Skill scanner not available \u2014 scan hook will pass through");
|
|
9716
|
+
});
|
|
9717
|
+
const safeFallback = (inputs) => ({
|
|
9718
|
+
verdict: "safe",
|
|
9719
|
+
score: 100,
|
|
9720
|
+
findings: [],
|
|
9721
|
+
scannedAt: /* @__PURE__ */ new Date(),
|
|
9722
|
+
filesScanned: inputs.map((i) => i.filename)
|
|
9723
|
+
});
|
|
9724
|
+
return (inputs) => {
|
|
9725
|
+
if (!resolved || !scanFn) return safeFallback(inputs);
|
|
9726
|
+
return scanFn(inputs);
|
|
9727
|
+
};
|
|
9728
|
+
}
|
|
9577
9729
|
var plugin = {
|
|
9578
9730
|
id: "deskfree",
|
|
9579
9731
|
name: "DeskFree",
|
|
@@ -9593,6 +9745,8 @@ var plugin = {
|
|
|
9593
9745
|
}
|
|
9594
9746
|
return allTools.length > 0 ? allTools : null;
|
|
9595
9747
|
});
|
|
9748
|
+
const scanner = createLazyScanner(api.logger);
|
|
9749
|
+
api.on("before_tool_call", createSkillScanHook(scanner, api.logger));
|
|
9596
9750
|
api.on("before_agent_start", (_event, ctx) => {
|
|
9597
9751
|
return { prependContext: getDeskFreeContext(ctx.sessionKey) };
|
|
9598
9752
|
});
|