@questionbase/deskfree 0.3.0-alpha.26 → 0.3.0-alpha.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +229 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4697,6 +4697,12 @@ var wrapper_default = import_websocket.default;
|
|
|
4697
4697
|
var activeTaskId = null;
|
|
4698
4698
|
var completedTaskId = null;
|
|
4699
4699
|
var inboundThreadId = null;
|
|
4700
|
+
function setActiveTaskId(taskId) {
|
|
4701
|
+
if (taskId === null && activeTaskId !== null) {
|
|
4702
|
+
completedTaskId = activeTaskId;
|
|
4703
|
+
}
|
|
4704
|
+
activeTaskId = taskId;
|
|
4705
|
+
}
|
|
4700
4706
|
function getActiveTaskId() {
|
|
4701
4707
|
return activeTaskId ?? completedTaskId ?? inboundThreadId;
|
|
4702
4708
|
}
|
|
@@ -8842,6 +8848,54 @@ function resolveAccountFromConfig(api) {
|
|
|
8842
8848
|
enabled: ch.enabled !== false
|
|
8843
8849
|
};
|
|
8844
8850
|
}
|
|
8851
|
+
function estimateTokens(text) {
|
|
8852
|
+
return Math.ceil(text.length / 4);
|
|
8853
|
+
}
|
|
8854
|
+
var MAX_TASK_CONTEXT_TOKENS = 8e3;
|
|
8855
|
+
var MAX_FULL_MESSAGES = 15;
|
|
8856
|
+
var MAX_FILE_CONTENT_CHARS = 4e3;
|
|
8857
|
+
function trimTaskContext(result) {
|
|
8858
|
+
const raw = result;
|
|
8859
|
+
const trimmed = {
|
|
8860
|
+
taskId: result.taskId,
|
|
8861
|
+
title: result.title,
|
|
8862
|
+
status: result.status,
|
|
8863
|
+
instructions: result.instructions
|
|
8864
|
+
};
|
|
8865
|
+
if (raw["substeps"]) {
|
|
8866
|
+
trimmed.substeps = raw["substeps"];
|
|
8867
|
+
}
|
|
8868
|
+
if (raw["parentContext"]) {
|
|
8869
|
+
trimmed.parentContext = raw["parentContext"];
|
|
8870
|
+
}
|
|
8871
|
+
if (result.messages && result.messages.length > 0) {
|
|
8872
|
+
if (result.messages.length <= MAX_FULL_MESSAGES) {
|
|
8873
|
+
trimmed.messages = result.messages;
|
|
8874
|
+
} else {
|
|
8875
|
+
const omitted = result.messages.length - MAX_FULL_MESSAGES;
|
|
8876
|
+
const recentMessages = result.messages.slice(-MAX_FULL_MESSAGES);
|
|
8877
|
+
trimmed.messages = recentMessages;
|
|
8878
|
+
trimmed.messagesOmitted = `${omitted} earlier message(s) omitted \u2014 use deskfree_send_message to ask about history if needed`;
|
|
8879
|
+
}
|
|
8880
|
+
}
|
|
8881
|
+
if (result.fileContext) {
|
|
8882
|
+
const fc = result.fileContext;
|
|
8883
|
+
if (fc.content.length > MAX_FILE_CONTENT_CHARS) {
|
|
8884
|
+
trimmed.fileContext = {
|
|
8885
|
+
fileId: fc.fileId,
|
|
8886
|
+
name: fc.name,
|
|
8887
|
+
description: fc.description,
|
|
8888
|
+
contentFormat: fc.contentFormat,
|
|
8889
|
+
version: fc.version,
|
|
8890
|
+
content: fc.content.slice(0, MAX_FILE_CONTENT_CHARS),
|
|
8891
|
+
contentTruncated: `Content truncated from ${fc.content.length} chars. Full content available \u2014 use deskfree_update_file with the complete replacement when editing.`
|
|
8892
|
+
};
|
|
8893
|
+
} else {
|
|
8894
|
+
trimmed.fileContext = fc;
|
|
8895
|
+
}
|
|
8896
|
+
}
|
|
8897
|
+
return trimmed;
|
|
8898
|
+
}
|
|
8845
8899
|
function formatTaskResponse(task, summary, nextActions) {
|
|
8846
8900
|
return {
|
|
8847
8901
|
content: [
|
|
@@ -8947,6 +9001,33 @@ function validateStringParam(params, key, required) {
|
|
|
8947
9001
|
}
|
|
8948
9002
|
return value;
|
|
8949
9003
|
}
|
|
9004
|
+
function validateEnumParam(params, key, allowedValues, required) {
|
|
9005
|
+
if (!params) {
|
|
9006
|
+
if (required) {
|
|
9007
|
+
throw new Error(
|
|
9008
|
+
`Required parameter '${key}' is missing (no params provided)`
|
|
9009
|
+
);
|
|
9010
|
+
}
|
|
9011
|
+
return void 0;
|
|
9012
|
+
}
|
|
9013
|
+
const value = params[key];
|
|
9014
|
+
if (value == null) {
|
|
9015
|
+
if (required) throw new Error(`Required parameter '${key}' is missing`);
|
|
9016
|
+
return void 0;
|
|
9017
|
+
}
|
|
9018
|
+
if (typeof value !== "string") {
|
|
9019
|
+
throw new Error(`Parameter '${key}' must be a string, got ${typeof value}`);
|
|
9020
|
+
}
|
|
9021
|
+
if (required && value.trim() === "") {
|
|
9022
|
+
throw new Error(`Required parameter '${key}' cannot be empty`);
|
|
9023
|
+
}
|
|
9024
|
+
if (!allowedValues.includes(value)) {
|
|
9025
|
+
throw new Error(
|
|
9026
|
+
`Parameter '${key}' must be one of: ${allowedValues.join(", ")}, got: ${value}`
|
|
9027
|
+
);
|
|
9028
|
+
}
|
|
9029
|
+
return value;
|
|
9030
|
+
}
|
|
8950
9031
|
function parseProposeTasks(raw) {
|
|
8951
9032
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
8952
9033
|
throw new Error('Parameter "tasks" must be a non-empty array');
|
|
@@ -9002,6 +9083,65 @@ function parseInitiative(raw) {
|
|
|
9002
9083
|
}
|
|
9003
9084
|
return void 0;
|
|
9004
9085
|
}
|
|
9086
|
+
function makeUpdateFileHandler(client) {
|
|
9087
|
+
return async (_id, params) => {
|
|
9088
|
+
try {
|
|
9089
|
+
const fileId = validateStringParam(params, "fileId", true);
|
|
9090
|
+
const content = validateStringParam(params, "content", true);
|
|
9091
|
+
const contentFormat = validateEnumParam(
|
|
9092
|
+
params,
|
|
9093
|
+
"contentFormat",
|
|
9094
|
+
["markdown", "html"],
|
|
9095
|
+
false
|
|
9096
|
+
);
|
|
9097
|
+
await client.updateFile({ fileId, content, contentFormat });
|
|
9098
|
+
return formatConfirmation(`Updated file ${fileId}`, [
|
|
9099
|
+
"File content has been saved",
|
|
9100
|
+
"Complete the task with deskfree_complete_task when ready"
|
|
9101
|
+
]);
|
|
9102
|
+
} catch (err) {
|
|
9103
|
+
return errorResult(err);
|
|
9104
|
+
}
|
|
9105
|
+
};
|
|
9106
|
+
}
|
|
9107
|
+
function makeCompleteTaskHandler(client) {
|
|
9108
|
+
return async (_id, params) => {
|
|
9109
|
+
try {
|
|
9110
|
+
const taskId = validateStringParam(params, "taskId", true);
|
|
9111
|
+
const outcome = validateEnumParam(
|
|
9112
|
+
params,
|
|
9113
|
+
"outcome",
|
|
9114
|
+
["done", "blocked"],
|
|
9115
|
+
true
|
|
9116
|
+
);
|
|
9117
|
+
const summary = validateStringParam(params, "summary", false);
|
|
9118
|
+
const evaluation = params?.evaluation;
|
|
9119
|
+
const result = await client.completeTask({
|
|
9120
|
+
taskId,
|
|
9121
|
+
outcome,
|
|
9122
|
+
summary,
|
|
9123
|
+
...evaluation ? { evaluation } : {}
|
|
9124
|
+
});
|
|
9125
|
+
setActiveTaskId(null);
|
|
9126
|
+
const summaryVerb = outcome === "done" ? "completed" : "blocked";
|
|
9127
|
+
const icon = outcome === "done" ? "\u2705" : "\u{1F6AB}";
|
|
9128
|
+
await client.sendMessage({
|
|
9129
|
+
content: `${icon} Task ${summaryVerb}: "${result.title}"`
|
|
9130
|
+
}).catch(() => {
|
|
9131
|
+
});
|
|
9132
|
+
return formatTaskResponse(
|
|
9133
|
+
result,
|
|
9134
|
+
`Task "${result.title}" marked as ${summaryVerb} \u2014 waiting for human`,
|
|
9135
|
+
[
|
|
9136
|
+
outcome === "done" ? "Human will review your work summary" : "Human will review the blocker and provide guidance",
|
|
9137
|
+
"Use deskfree_state to check for other tasks"
|
|
9138
|
+
]
|
|
9139
|
+
);
|
|
9140
|
+
} catch (err) {
|
|
9141
|
+
return errorResult(err);
|
|
9142
|
+
}
|
|
9143
|
+
};
|
|
9144
|
+
}
|
|
9005
9145
|
function makeReopenTaskHandler(client) {
|
|
9006
9146
|
return async (_id, params) => {
|
|
9007
9147
|
try {
|
|
@@ -9133,6 +9273,84 @@ function createOrchestratorTools(api) {
|
|
|
9133
9273
|
}
|
|
9134
9274
|
];
|
|
9135
9275
|
}
|
|
9276
|
+
function createWorkerTools(api) {
|
|
9277
|
+
const account = resolveAccountFromConfig(api);
|
|
9278
|
+
if (!account) return null;
|
|
9279
|
+
const client = new DeskFreeClient(account.botToken, account.apiUrl);
|
|
9280
|
+
return [
|
|
9281
|
+
{
|
|
9282
|
+
...WORKER_TOOLS.START_TASK,
|
|
9283
|
+
async execute(_id, params) {
|
|
9284
|
+
try {
|
|
9285
|
+
const taskId = validateStringParam(params, "taskId", true);
|
|
9286
|
+
const runnerId = validateStringParam(params, "runnerId", false);
|
|
9287
|
+
const result = await client.claimTask({ taskId, runnerId });
|
|
9288
|
+
setActiveTaskId(taskId);
|
|
9289
|
+
let skillInstructions = "";
|
|
9290
|
+
if (result.skillContext?.length) {
|
|
9291
|
+
skillInstructions = result.skillContext.map(
|
|
9292
|
+
(s) => `
|
|
9293
|
+
\u26A0\uFE0F SKILL: ${s.displayName}
|
|
9294
|
+
${s.criticalSection}
|
|
9295
|
+
|
|
9296
|
+
${s.instructions}`
|
|
9297
|
+
).join("\n\n---\n");
|
|
9298
|
+
}
|
|
9299
|
+
const trimmedTask = trimTaskContext(result);
|
|
9300
|
+
const taskJson = JSON.stringify(
|
|
9301
|
+
{
|
|
9302
|
+
summary: `Claimed task "${result.title}" \u2014 full context loaded${result.skillContext?.length ? ` (${result.skillContext.length} skill${result.skillContext.length > 1 ? "s" : ""} active)` : ""}`,
|
|
9303
|
+
mode: trimmedTask.mode ?? "work",
|
|
9304
|
+
nextActions: [
|
|
9305
|
+
"Read the instructions and message history carefully",
|
|
9306
|
+
...result.fileContext ? [
|
|
9307
|
+
`Task has a linked file "${result.fileContext.name}" (ID: ${result.fileContext.fileId}) \u2014 use deskfree_update_file to save your work to it`
|
|
9308
|
+
] : [],
|
|
9309
|
+
...trimmedTask.mode === "evaluation" ? [
|
|
9310
|
+
"This is an evaluation task \u2014 review the WoW context and provide your evaluation in deskfree_complete_task"
|
|
9311
|
+
] : [],
|
|
9312
|
+
"Complete with deskfree_complete_task when done (summary required)"
|
|
9313
|
+
],
|
|
9314
|
+
task: trimmedTask
|
|
9315
|
+
},
|
|
9316
|
+
null,
|
|
9317
|
+
2
|
|
9318
|
+
);
|
|
9319
|
+
const totalTokens = estimateTokens(skillInstructions) + estimateTokens(taskJson);
|
|
9320
|
+
const budgetWarning = totalTokens > MAX_TASK_CONTEXT_TOKENS ? `
|
|
9321
|
+
\u26A0\uFE0F Context budget: ~${totalTokens} tokens loaded (~${MAX_TASK_CONTEXT_TOKENS} target). Be concise in your reasoning.` : "";
|
|
9322
|
+
return {
|
|
9323
|
+
content: [
|
|
9324
|
+
...skillInstructions ? [{ type: "text", text: skillInstructions }] : [],
|
|
9325
|
+
{
|
|
9326
|
+
type: "text",
|
|
9327
|
+
text: taskJson + budgetWarning
|
|
9328
|
+
}
|
|
9329
|
+
]
|
|
9330
|
+
};
|
|
9331
|
+
} catch (err) {
|
|
9332
|
+
return errorResult(err);
|
|
9333
|
+
}
|
|
9334
|
+
}
|
|
9335
|
+
},
|
|
9336
|
+
{
|
|
9337
|
+
...WORKER_TOOLS.UPDATE_FILE,
|
|
9338
|
+
execute: makeUpdateFileHandler(client)
|
|
9339
|
+
},
|
|
9340
|
+
{
|
|
9341
|
+
...WORKER_TOOLS.COMPLETE_TASK,
|
|
9342
|
+
execute: makeCompleteTaskHandler(client)
|
|
9343
|
+
},
|
|
9344
|
+
{
|
|
9345
|
+
...WORKER_TOOLS.SEND_MESSAGE,
|
|
9346
|
+
execute: makeSendMessageHandler(client)
|
|
9347
|
+
},
|
|
9348
|
+
{
|
|
9349
|
+
...WORKER_TOOLS.PROPOSE,
|
|
9350
|
+
execute: makeProposeHandler(client)
|
|
9351
|
+
}
|
|
9352
|
+
];
|
|
9353
|
+
}
|
|
9136
9354
|
|
|
9137
9355
|
// src/offline-queue.ts
|
|
9138
9356
|
var MAX_QUEUE_SIZE2 = 100;
|
|
@@ -9248,7 +9466,17 @@ var plugin = {
|
|
|
9248
9466
|
setDeskFreeRuntime(api.runtime);
|
|
9249
9467
|
api.registerChannel({ plugin: deskFreePlugin });
|
|
9250
9468
|
api.registerTool(() => {
|
|
9251
|
-
|
|
9469
|
+
const orchestratorTools = createOrchestratorTools(api) ?? [];
|
|
9470
|
+
const workerTools = createWorkerTools(api) ?? [];
|
|
9471
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9472
|
+
const allTools = [];
|
|
9473
|
+
for (const tool of [...orchestratorTools, ...workerTools]) {
|
|
9474
|
+
if (!seen.has(tool.name)) {
|
|
9475
|
+
seen.add(tool.name);
|
|
9476
|
+
allTools.push(tool);
|
|
9477
|
+
}
|
|
9478
|
+
}
|
|
9479
|
+
return allTools.length > 0 ? allTools : null;
|
|
9252
9480
|
});
|
|
9253
9481
|
api.on("before_agent_start", (_event, ctx) => {
|
|
9254
9482
|
return { prependContext: getDeskFreeContext(ctx.sessionKey) };
|