@questionbase/deskfree 0.3.0-alpha.25 → 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 +231 -2
- 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
|
}
|
|
@@ -5202,7 +5208,7 @@ async function pollAndDeliver(client, ctx, cursor, log, account) {
|
|
|
5202
5208
|
try {
|
|
5203
5209
|
const botName = account?.botName;
|
|
5204
5210
|
const humanName = account?.humanName;
|
|
5205
|
-
const welcomeContent = botName && humanName ? `Hey ${botName}! I'm ${humanName}. We're connected through DeskFree \u2014 I'll send you tasks and you'll help me get things done. What should we work on first
|
|
5211
|
+
const welcomeContent = botName && humanName ? `Hey ${botName}! I'm ${humanName}. We're connected through DeskFree \u2014 I'll send you tasks and you'll help me get things done. What should we work on first? (Just reply to this \u2014 don't introduce yourself again on heartbeat, wait for my response.)` : "DeskFree is connected! Send me tasks and I'll help you get things done. What should we work on first? (Just reply to this \u2014 don't introduce yourself again on heartbeat, wait for my response.)";
|
|
5206
5212
|
const welcomeMessage = {
|
|
5207
5213
|
messageId: `welcome-${Date.now()}`,
|
|
5208
5214
|
botId: "",
|
|
@@ -8805,6 +8811,7 @@ Tools: deskfree_start_task, deskfree_update_file, deskfree_complete_task, deskfr
|
|
|
8805
8811
|
- Claim your task first with deskfree_start_task \u2014 this loads instructions, messages, and file context.
|
|
8806
8812
|
- Save work to linked files with deskfree_update_file (incrementally).
|
|
8807
8813
|
- Complete with deskfree_complete_task when done (summary required for outcome "done"). Include learnings if applicable.
|
|
8814
|
+
- If you need human input to proceed, send a message explaining what you need, then complete with outcome "review" \u2014 this marks the task as needing attention.
|
|
8808
8815
|
- Propose follow-up tasks with deskfree_propose if you discover more work.`;
|
|
8809
8816
|
function getDeskFreeContext(sessionKey) {
|
|
8810
8817
|
const isWorker = sessionKey && (sessionKey.includes(":sub:") || sessionKey.includes(":spawn:") || sessionKey.includes(":run:"));
|
|
@@ -8841,6 +8848,54 @@ function resolveAccountFromConfig(api) {
|
|
|
8841
8848
|
enabled: ch.enabled !== false
|
|
8842
8849
|
};
|
|
8843
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
|
+
}
|
|
8844
8899
|
function formatTaskResponse(task, summary, nextActions) {
|
|
8845
8900
|
return {
|
|
8846
8901
|
content: [
|
|
@@ -8946,6 +9001,33 @@ function validateStringParam(params, key, required) {
|
|
|
8946
9001
|
}
|
|
8947
9002
|
return value;
|
|
8948
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
|
+
}
|
|
8949
9031
|
function parseProposeTasks(raw) {
|
|
8950
9032
|
if (!Array.isArray(raw) || raw.length === 0) {
|
|
8951
9033
|
throw new Error('Parameter "tasks" must be a non-empty array');
|
|
@@ -9001,6 +9083,65 @@ function parseInitiative(raw) {
|
|
|
9001
9083
|
}
|
|
9002
9084
|
return void 0;
|
|
9003
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
|
+
}
|
|
9004
9145
|
function makeReopenTaskHandler(client) {
|
|
9005
9146
|
return async (_id, params) => {
|
|
9006
9147
|
try {
|
|
@@ -9132,6 +9273,84 @@ function createOrchestratorTools(api) {
|
|
|
9132
9273
|
}
|
|
9133
9274
|
];
|
|
9134
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
|
+
}
|
|
9135
9354
|
|
|
9136
9355
|
// src/offline-queue.ts
|
|
9137
9356
|
var MAX_QUEUE_SIZE2 = 100;
|
|
@@ -9247,7 +9466,17 @@ var plugin = {
|
|
|
9247
9466
|
setDeskFreeRuntime(api.runtime);
|
|
9248
9467
|
api.registerChannel({ plugin: deskFreePlugin });
|
|
9249
9468
|
api.registerTool(() => {
|
|
9250
|
-
|
|
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;
|
|
9251
9480
|
});
|
|
9252
9481
|
api.on("before_agent_start", (_event, ctx) => {
|
|
9253
9482
|
return { prependContext: getDeskFreeContext(ctx.sessionKey) };
|