@joshski/dust 0.1.90 → 0.1.91

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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Tool execution protocol types for the WebSocket messages between
3
+ * dust clients and dustbucket servers.
4
+ *
5
+ * These types define the wire format. Both sides must agree on this protocol.
6
+ * Import from '@joshski/dust/types' for downstream implementations.
7
+ */
8
+ /**
9
+ * Client-to-server message requesting tool execution.
10
+ *
11
+ * Sent over the bucket WebSocket when an agent invokes `dust bucket tool <name>`.
12
+ * The `tool` field identifies the server-defined tool by name.
13
+ * The `arguments` field contains named parameters matching the tool definition.
14
+ * The `repositoryId` identifies the repository context (numeric server-side ID).
15
+ */
16
+ export interface ToolExecutionRequestMessage {
17
+ type: 'tool-execution-request';
18
+ requestId: string;
19
+ tool: string;
20
+ repositoryId: number;
21
+ arguments: Record<string, unknown>;
22
+ }
23
+ export interface ToolExecutionSuccessResult {
24
+ type: 'success';
25
+ data: unknown;
26
+ }
27
+ export interface ToolExecutionToolNotFoundResult {
28
+ type: 'tool-not-found';
29
+ message: string;
30
+ }
31
+ export interface ToolExecutionErrorResult {
32
+ type: 'error';
33
+ message: string;
34
+ }
35
+ export type ToolExecutionResult = ToolExecutionSuccessResult | ToolExecutionToolNotFoundResult | ToolExecutionErrorResult;
36
+ /**
37
+ * Server-to-client message with the result of a tool execution request.
38
+ *
39
+ * The `requestId` correlates this response to the original request.
40
+ * The `result` is a discriminated union on the `type` field.
41
+ */
42
+ export interface ToolExecutionResultMessage {
43
+ type: 'tool-execution-result';
44
+ requestId: string;
45
+ result: ToolExecutionResult;
46
+ }
47
+ export declare function isToolExecutionResultMessage(payload: unknown): payload is ToolExecutionResultMessage;
48
+ export declare function isToolExecutionRequestMessage(payload: unknown): payload is ToolExecutionRequestMessage;
package/dist/dust.js CHANGED
@@ -500,7 +500,7 @@ async function loadSettings(cwd, fileSystem) {
500
500
  }
501
501
 
502
502
  // lib/version.ts
503
- var DUST_VERSION = "0.1.90";
503
+ var DUST_VERSION = "0.1.91";
504
504
 
505
505
  // lib/session.ts
506
506
  var DUST_UNATTENDED = "DUST_UNATTENDED";
@@ -4570,8 +4570,11 @@ function formatToolsSection(tools) {
4570
4570
  return "";
4571
4571
  }
4572
4572
  const lines = [];
4573
+ lines.push("");
4573
4574
  lines.push("## Available Tools");
4574
4575
  lines.push("");
4576
+ lines.push("Use these tools where it makes sense in the execution of this task:");
4577
+ lines.push("");
4575
4578
  for (const tool of tools) {
4576
4579
  lines.push(formatTool(tool));
4577
4580
  lines.push("");
@@ -5440,6 +5443,18 @@ var SGR_MOUSE_RE2 = new RegExp(String.raw`^\x1b\[<(\d+);\d+;\d+[Mm]$`);
5440
5443
  function isRecord3(value) {
5441
5444
  return typeof value === "object" && value !== null;
5442
5445
  }
5446
+ function isValidResultType(value) {
5447
+ return value === "success" || value === "tool-not-found" || value === "error";
5448
+ }
5449
+ function isValidResult(value) {
5450
+ if (!isRecord3(value))
5451
+ return false;
5452
+ if (!isValidResultType(value.type))
5453
+ return false;
5454
+ if (value.type === "success")
5455
+ return true;
5456
+ return typeof value.message === "string";
5457
+ }
5443
5458
  function isToolExecutionResultMessage(payload) {
5444
5459
  if (!isRecord3(payload))
5445
5460
  return false;
@@ -5448,16 +5463,7 @@ function isToolExecutionResultMessage(payload) {
5448
5463
  if (typeof payload.requestId !== "string" || payload.requestId.length === 0) {
5449
5464
  return false;
5450
5465
  }
5451
- if (payload.status !== "success" && payload.status !== "tool-not-found" && payload.status !== "error") {
5452
- return false;
5453
- }
5454
- if (payload.output !== undefined && typeof payload.output !== "string") {
5455
- return false;
5456
- }
5457
- if (payload.error !== undefined && typeof payload.error !== "string") {
5458
- return false;
5459
- }
5460
- return true;
5466
+ return isValidResult(payload.result);
5461
5467
  }
5462
5468
 
5463
5469
  // lib/cli/commands/bucket.ts
@@ -6147,12 +6153,23 @@ async function bucketWorker(dependencies, bucketDeps = createDefaultBucketDepend
6147
6153
  });
6148
6154
  }
6149
6155
  const requestId = crypto.randomUUID();
6156
+ const toolDef = state.tools.find((t) => t.name === request.toolName);
6157
+ const namedArgs = {};
6158
+ if (toolDef) {
6159
+ for (let i = 0;i < toolDef.parameters.length && i < request.arguments.length; i++) {
6160
+ namedArgs[toolDef.parameters[i].name] = request.arguments[i];
6161
+ }
6162
+ } else {
6163
+ for (let i = 0;i < request.arguments.length; i++) {
6164
+ namedArgs[`arg${i}`] = request.arguments[i];
6165
+ }
6166
+ }
6150
6167
  const message = {
6151
6168
  type: "tool-execution-request",
6152
6169
  requestId,
6153
- toolName: request.toolName,
6154
- arguments: request.arguments,
6155
- repositoryId: request.repositoryId
6170
+ tool: request.toolName,
6171
+ repositoryId: Number(request.repositoryId),
6172
+ arguments: namedArgs
6156
6173
  };
6157
6174
  log10(`forwarding tool execution: ${request.toolName} requestId=${requestId}`);
6158
6175
  return new Promise((resolve, reject) => {
@@ -6198,14 +6215,30 @@ async function bucketWorker(dependencies, bucketDeps = createDefaultBucketDepend
6198
6215
  log10(`received tool execution result for unknown requestId=${message.requestId}`);
6199
6216
  return;
6200
6217
  }
6201
- log10(`received tool execution result: requestId=${message.requestId} status=${message.status}`);
6218
+ const resultType = message.result.type;
6219
+ log10(`received tool execution result: requestId=${message.requestId} status=${resultType}`);
6202
6220
  clearTimeout(pending.timeoutId);
6203
6221
  pendingToolExecutions.delete(message.requestId);
6204
- pending.resolve({
6205
- status: message.status,
6206
- output: message.output,
6207
- error: message.error
6208
- });
6222
+ switch (message.result.type) {
6223
+ case "success":
6224
+ pending.resolve({
6225
+ status: "success",
6226
+ output: typeof message.result.data === "string" ? message.result.data : JSON.stringify(message.result.data)
6227
+ });
6228
+ break;
6229
+ case "tool-not-found":
6230
+ pending.resolve({
6231
+ status: "tool-not-found",
6232
+ error: message.result.message
6233
+ });
6234
+ break;
6235
+ case "error":
6236
+ pending.resolve({
6237
+ status: "error",
6238
+ error: message.result.message
6239
+ });
6240
+ break;
6241
+ }
6209
6242
  }, forwardToolExecution);
6210
6243
  if (!useTUI) {
6211
6244
  context.stdout(" Press q or Ctrl+C to exit");
@@ -6895,14 +6928,22 @@ function validateLinks(filePath, content, fileSystem) {
6895
6928
  while (match) {
6896
6929
  const linkTarget = match[2];
6897
6930
  if (!linkTarget.startsWith("http://") && !linkTarget.startsWith("https://") && !linkTarget.startsWith("#")) {
6898
- const targetPath = linkTarget.split("#")[0];
6899
- const resolvedPath = resolve(fileDir, targetPath);
6900
- if (!fileSystem.exists(resolvedPath)) {
6931
+ if (linkTarget.startsWith("/")) {
6901
6932
  violations.push({
6902
6933
  file: filePath,
6903
- message: `Broken link: "${linkTarget}"`,
6934
+ message: `Absolute link not allowed: "${linkTarget}" (use a relative path instead)`,
6904
6935
  line: i + 1
6905
6936
  });
6937
+ } else {
6938
+ const targetPath = linkTarget.split("#")[0];
6939
+ const resolvedPath = resolve(fileDir, targetPath);
6940
+ if (!fileSystem.exists(resolvedPath)) {
6941
+ violations.push({
6942
+ file: filePath,
6943
+ message: `Broken link: "${linkTarget}"`,
6944
+ line: i + 1
6945
+ });
6946
+ }
6906
6947
  }
6907
6948
  }
6908
6949
  match = linkPattern.exec(line);
package/dist/types.d.ts CHANGED
@@ -10,3 +10,5 @@ export type { ArtifactType, TaskGraph, TaskGraphNode } from './artifacts/index';
10
10
  export type { Task } from './artifacts/tasks';
11
11
  export type { CreateIdeaTransitionTaskResult, DecomposeIdeaOptions, IdeaInProgress, OpenQuestionResponse, ParsedCaptureIdeaTask, WorkflowTaskMatch, WorkflowTaskType, } from './artifacts/workflow-tasks';
12
12
  export type { Repository } from './bucket/repository';
13
+ export type { ToolExecutionErrorResult, ToolExecutionRequestMessage, ToolExecutionResult, ToolExecutionResultMessage, ToolExecutionSuccessResult, ToolExecutionToolNotFoundResult, } from './bucket/tool-execution-protocol';
14
+ export { isToolExecutionRequestMessage, isToolExecutionResultMessage, } from './bucket/tool-execution-protocol';
@@ -451,14 +451,22 @@ function validateLinks(filePath, content, fileSystem) {
451
451
  while (match) {
452
452
  const linkTarget = match[2];
453
453
  if (!linkTarget.startsWith("http://") && !linkTarget.startsWith("https://") && !linkTarget.startsWith("#")) {
454
- const targetPath = linkTarget.split("#")[0];
455
- const resolvedPath = resolve(fileDir, targetPath);
456
- if (!fileSystem.exists(resolvedPath)) {
454
+ if (linkTarget.startsWith("/")) {
457
455
  violations.push({
458
456
  file: filePath,
459
- message: `Broken link: "${linkTarget}"`,
457
+ message: `Absolute link not allowed: "${linkTarget}" (use a relative path instead)`,
460
458
  line: i + 1
461
459
  });
460
+ } else {
461
+ const targetPath = linkTarget.split("#")[0];
462
+ const resolvedPath = resolve(fileDir, targetPath);
463
+ if (!fileSystem.exists(resolvedPath)) {
464
+ violations.push({
465
+ file: filePath,
466
+ message: `Broken link: "${linkTarget}"`,
467
+ line: i + 1
468
+ });
469
+ }
462
470
  }
463
471
  }
464
472
  match = linkPattern.exec(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshski/dust",
3
- "version": "0.1.90",
3
+ "version": "0.1.91",
4
4
  "description": "Flow state for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {