@fungi.computer/knapsack 0.2.1 → 0.3.0

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/files.d.ts CHANGED
@@ -5,7 +5,7 @@ import { z } from "zod";
5
5
  import { readInputSchema, readGuestOutputSchema, readOutputSchema } from "./files-read.js";
6
6
  export { readGuestOutputSchema, readInputSchema, readOutputSchema };
7
7
  /** Typed refusal from a bounded or conditional Files edit. */
8
- export type FilesEditErrorReason = "not-found" | "ambiguous" | "file-too-large" | "partial-character" | "unchanged" | "not-text" | "conflict";
8
+ export type FilesEditErrorReason = "not-found" | "ambiguous" | "file-too-large" | "partial-character" | "unchanged" | "not-text" | "conflict" | "read-only";
9
9
  /** Expected Files edit refusal, kept distinct from unexpected host failures. */
10
10
  export declare class FilesEditError extends Error {
11
11
  readonly reason: FilesEditErrorReason;
@@ -68,6 +68,7 @@ export declare const filesEditFailureSchema: z.ZodLazy<z.ZodObject<{
68
68
  unchanged: "unchanged";
69
69
  "not-text": "not-text";
70
70
  conflict: "conflict";
71
+ "read-only": "read-only";
71
72
  }>;
72
73
  path: z.ZodLazy<z.ZodString>;
73
74
  message: z.ZodString;
@@ -95,6 +96,7 @@ export declare const editOutputSchema: z.ZodUnion<readonly [z.ZodLazy<z.ZodObjec
95
96
  unchanged: "unchanged";
96
97
  "not-text": "not-text";
97
98
  conflict: "conflict";
99
+ "read-only": "read-only";
98
100
  }>;
99
101
  path: z.ZodLazy<z.ZodString>;
100
102
  message: z.ZodString;
@@ -104,6 +106,8 @@ export declare const editOutputSchema: z.ZodUnion<readonly [z.ZodLazy<z.ZodObjec
104
106
  export type FilesPatch = (input: z.output<typeof patchInputSchema>, context: InvocationContext) => Promise<z.output<typeof patchOutputSchema>>;
105
107
  /** Captured host file authority; the host owns path confinement and atomic replacement. */
106
108
  export type FilesPort = Readonly<{
109
+ /** Host-selected mode gate, before an edit reads or computes a replacement. */
110
+ assertEditable?: (path: string) => void;
107
111
  /** Omitted when this Files host has no qualified native patch engine. */
108
112
  patch?: FilesPatch;
109
113
  /** Read a bounded text page. */
package/dist/files.js CHANGED
@@ -97,6 +97,7 @@ export const filesEditFailureSchema = z.lazy(() => z.strictObject({
97
97
  "not-text",
98
98
  "unchanged",
99
99
  "conflict",
100
+ "read-only",
100
101
  ]),
101
102
  path: pathSchema,
102
103
  message: z.string(),
@@ -255,6 +256,7 @@ function generateDiffString(oldContent, newContent, context = 4) {
255
256
  return { diff: output.join("\n"), firstChangedLine };
256
257
  }
257
258
  async function applyEdit(port, input, context) {
259
+ port.assertEditable?.(input.path);
258
260
  const current = readOutputSchema.parse(await port.read({
259
261
  path: input.path,
260
262
  offset: 0,
@@ -321,9 +323,16 @@ export async function edit(port, input, context) {
321
323
  }
322
324
  }
323
325
  /** Cancellation does not finish until the Files owner has acknowledged cleanup. */
324
- function runFileOperation(invoke) {
326
+ function runFileOperation(invoke, toolCallId) {
325
327
  return Effect.callback((resume, signal) => {
326
- const pending = invoke({ executionId: nanoid(), deadlineAt: null, signal });
328
+ const context = {
329
+ executionId: toolCallId ?? nanoid(),
330
+ deadlineAt: null,
331
+ signal,
332
+ };
333
+ if (toolCallId !== undefined)
334
+ Object.assign(context, { toolCallId });
335
+ const pending = invoke(context);
327
336
  void pending.then((result) => {
328
337
  resume(Effect.succeed(result));
329
338
  }, (cause) => {
@@ -336,8 +345,8 @@ function runFileOperation(invoke) {
336
345
  return Effect.promise(() => pending.then(() => undefined, () => undefined));
337
346
  });
338
347
  }
339
- function runPatch(invoke, input) {
340
- return runFileOperation((context) => invoke(input, context));
348
+ function runPatch(invoke, input, toolCallId) {
349
+ return runFileOperation((context) => invoke(input, context), toolCallId);
341
350
  }
342
351
  /** Keep model parameters while omitting the decoder's schema-document marker. */
343
352
  function toolParameters(schema) {
@@ -369,7 +378,7 @@ function patchTool(invokePatch) {
369
378
  message: cause instanceof Error ? cause.message : "Invalid patch input",
370
379
  }),
371
380
  }).pipe(Effect.map((value) => ({
372
- execute: () => Stream.fromEffect(runPatch(invokePatch, value).pipe(Effect.map((result) => ({
381
+ execute: () => Stream.fromEffect(runPatch(invokePatch, value, call.id).pipe(Effect.map((result) => ({
373
382
  type: "result",
374
383
  result: {
375
384
  content: [
@@ -378,7 +387,7 @@ function patchTool(invokePatch) {
378
387
  text: JSON.stringify(result),
379
388
  },
380
389
  ],
381
- isError: result.status !== "completed",
390
+ isError: "status" in result && result.status !== "completed",
382
391
  },
383
392
  })))),
384
393
  }))),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/knapsack",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "description": "Agent tool connections projected into Mycelium capability sources",
6
6
  "type": "module",
@@ -47,8 +47,8 @@
47
47
  "dependencies": {
48
48
  "@fungi.computer/cairn": "0.1.0",
49
49
  "@fungi.computer/forage": "0.2.0",
50
- "@fungi.computer/mule": "0.1.1",
51
- "@fungi.computer/mycelium": "0.2.1",
50
+ "@fungi.computer/mule": "0.2.0",
51
+ "@fungi.computer/mycelium": "0.2.2",
52
52
  "@fungi.computer/whistle": "0.1.0",
53
53
  "@modelcontextprotocol/client": "2.0.0",
54
54
  "diff": "8.0.4",