@fungi.computer/shiitake-extensions 0.1.0 → 0.2.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/goal/host.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Mycelium } from "@fungi.computer/mycelium";
1
+ import { CapabilityFailed, Mycelium, } from "@fungi.computer/mycelium";
2
2
  import { defineCollection, defineExtension, ExtensionCommandError, ExtensionStop, } from "@fungi.computer/shiitake/extensions";
3
3
  import * as Cause from "effect/Cause";
4
4
  import * as Clock from "effect/Clock";
@@ -245,17 +245,24 @@ function settle(ctx, run) {
245
245
  yield* ctx.observe(run.sessionId);
246
246
  });
247
247
  }
248
- /** Run one tool transaction and surface its failure to the model as an Error. */
249
- function toolCall(ctx, body) {
248
+ /** Run one tool transaction and preserve its owner-admitted diagnostic. */
249
+ function toolCall(ctx, operation, body) {
250
250
  return Effect.runPromiseExit(ctx.transact(body)).then((exit) => {
251
251
  if (Exit.isSuccess(exit))
252
252
  return exit.value;
253
253
  const failure = Cause.squash(exit.cause);
254
- return Promise.reject(new Error(failure instanceof ExtensionStop
255
- ? failure.reason
256
- : failure instanceof Error
257
- ? failure.message
258
- : "Goal tool failed"));
254
+ return Promise.reject(new CapabilityFailed({
255
+ namespace: "goal",
256
+ operation,
257
+ cause: {
258
+ kind: "OperationFailed",
259
+ message: failure instanceof ExtensionStop
260
+ ? failure.reason
261
+ : failure instanceof Error
262
+ ? failure.message
263
+ : "Goal tool failed",
264
+ },
265
+ }));
259
266
  });
260
267
  }
261
268
  // Mycelium's maintained operation interface takes Zod for model-tool I/O.
@@ -276,7 +283,7 @@ function goalModule(ctx, sessionId, workId) {
276
283
  objective: toolObjective,
277
284
  state: z.literal("active"),
278
285
  }),
279
- execute: ({ objective }) => toolCall(ctx, Effect.gen(function* () {
286
+ execute: ({ objective }) => toolCall(ctx, "create_goal", Effect.gen(function* () {
280
287
  const decoded = yield* Schema.decodeUnknownEffect(Objective)(objective).pipe(Effect.mapError(() => conflict("Goal objective is invalid")));
281
288
  if (Option.isSome(yield* ctx.state.goals.get(sessionId)))
282
289
  return yield* Effect.fail(conflict("A Session goal is already active or paused; only a person replaces it"));
@@ -308,7 +315,7 @@ function goalModule(ctx, sessionId, workId) {
308
315
  }),
309
316
  z.null(),
310
317
  ]),
311
- execute: () => toolCall(ctx, ctx.state.goals.get(sessionId).pipe(Effect.map((found) => Option.isNone(found)
318
+ execute: () => toolCall(ctx, "get_goal", ctx.state.goals.get(sessionId).pipe(Effect.map((found) => Option.isNone(found)
312
319
  ? null
313
320
  : {
314
321
  objective: found.value.objective,
@@ -330,7 +337,7 @@ function goalModule(ctx, sessionId, workId) {
330
337
  state: z.enum(["complete", "blocked"]),
331
338
  reason: z.string(),
332
339
  }),
333
- execute: ({ status, reason }) => toolCall(ctx, Effect.gen(function* () {
340
+ execute: ({ status, reason }) => toolCall(ctx, "update_goal", Effect.gen(function* () {
334
341
  const decoded = yield* Schema.decodeUnknownEffect(GoalReason)(reason).pipe(Effect.mapError(() => conflict("Goal reason is invalid")));
335
342
  const found = yield* ctx.state.goals.get(sessionId);
336
343
  if (Option.isNone(found) ||
@@ -14,8 +14,13 @@ export declare const homeGrant: ExtensionGrantV0;
14
14
  /** Agent-scope choice with per-run tools and prompt over one captured table. */
15
15
  export declare function homeExtension(options: {
16
16
  computers: {
17
+ /** Read the table inside the synchronous Home choice transaction. */
17
18
  read: (context: Readonly<{
18
19
  sessionId: string;
19
20
  }>) => readonly HomeComputer[];
21
+ /** Optional host preparation before run-scoped Files capture. */
22
+ prepare?: (context: Readonly<{
23
+ sessionId: string;
24
+ }>) => Promise<void>;
20
25
  };
21
26
  }): RosteredExtensionV0;
package/dist/home/host.js CHANGED
@@ -51,7 +51,8 @@ export function homeExtension(options) {
51
51
  schema: Schema.NullOr(Home),
52
52
  read: (ctx) => Option.some(Option.getOrNull(ctx.state.choice.peek("default"))),
53
53
  },
54
- files: (ctx, run) => {
54
+ files: async (ctx, run) => {
55
+ await options.computers.prepare?.(run);
55
56
  const computers = [...options.computers.read(run)];
56
57
  const choice = ctx.state.update((state) => {
57
58
  const recorded = Option.getOrNull(state.choice.get("default"));
@@ -35,6 +35,8 @@ export declare const Loop: Schema.Struct<{
35
35
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
36
36
  /** Visible explanation when loop work failed. */
37
37
  readonly lastError: Schema.NullOr<Schema.String>;
38
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
39
+ readonly consecutiveFailures: Schema.Int;
38
40
  }>;
39
41
  /** One Session's loop. */
40
42
  export type Loop = Schema.Schema.Type<typeof Loop>;
@@ -85,6 +87,8 @@ export declare const LoopProjection: Schema.Struct<{
85
87
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
86
88
  /** Visible explanation when loop work failed. */
87
89
  readonly lastError: Schema.NullOr<Schema.String>;
90
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
91
+ readonly consecutiveFailures: Schema.Int;
88
92
  }>;
89
93
  }>;
90
94
  /** Session-owned loop controls and their visible state. */
@@ -154,6 +158,8 @@ export declare const loopContract: {
154
158
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
155
159
  /** Visible explanation when loop work failed. */
156
160
  readonly lastError: Schema.NullOr<Schema.String>;
161
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
162
+ readonly consecutiveFailures: Schema.Int;
157
163
  }>;
158
164
  /** Admission receipt for the start command. */
159
165
  readonly accepted: Schema.Struct<{
@@ -210,6 +216,8 @@ export declare const loopContract: {
210
216
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
211
217
  /** Visible explanation when loop work failed. */
212
218
  readonly lastError: Schema.NullOr<Schema.String>;
219
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
220
+ readonly consecutiveFailures: Schema.Int;
213
221
  }>;
214
222
  /** Admission receipt for the start command. */
215
223
  readonly accepted: Schema.Struct<{
@@ -255,6 +263,8 @@ export declare const loopContract: {
255
263
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
256
264
  /** Visible explanation when loop work failed. */
257
265
  readonly lastError: Schema.NullOr<Schema.String>;
266
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
267
+ readonly consecutiveFailures: Schema.Int;
258
268
  }>;
259
269
  };
260
270
  /** Resume the current paused loop. */
@@ -292,6 +302,8 @@ export declare const loopContract: {
292
302
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
293
303
  /** Visible explanation when loop work failed. */
294
304
  readonly lastError: Schema.NullOr<Schema.String>;
305
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
306
+ readonly consecutiveFailures: Schema.Int;
295
307
  }>;
296
308
  };
297
309
  /** Stop future occurrences of the current loop. */
@@ -329,6 +341,8 @@ export declare const loopContract: {
329
341
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
330
342
  /** Visible explanation when loop work failed. */
331
343
  readonly lastError: Schema.NullOr<Schema.String>;
344
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
345
+ readonly consecutiveFailures: Schema.Int;
332
346
  }>;
333
347
  };
334
348
  };
@@ -365,6 +379,8 @@ export declare const loopContract: {
365
379
  readonly nextDueAt: Schema.NullOr<Schema.Int>;
366
380
  /** Visible explanation when loop work failed. */
367
381
  readonly lastError: Schema.NullOr<Schema.String>;
382
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
383
+ readonly consecutiveFailures: Schema.Int;
368
384
  }>;
369
385
  }>;
370
386
  };
@@ -45,6 +45,8 @@ export const Loop = Schema.Struct({
45
45
  nextDueAt: Schema.NullOr(Schema.Int),
46
46
  /** Visible explanation when loop work failed. */
47
47
  lastError: Schema.NullOr(Schema.String),
48
+ /** Consecutive failed work outcomes; successful work resets this to zero. */
49
+ consecutiveFailures: Schema.Int.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0))),
48
50
  });
49
51
  const StartResult = Schema.Struct({
50
52
  /** Newly started loop. */
package/dist/loop/host.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Mycelium } from "@fungi.computer/mycelium";
1
+ import { CapabilityFailed, Mycelium, } from "@fungi.computer/mycelium";
2
2
  import { defineCollection, defineExtension, ExtensionCommandError, ExtensionStop, } from "@fungi.computer/shiitake/extensions";
3
3
  import * as Cause from "effect/Cause";
4
4
  import * as Clock from "effect/Clock";
@@ -12,7 +12,7 @@ import { ask, catalog, done, freshRequestId } from "../internal/catalog.js";
12
12
  import { nextOccurrence } from "../internal/cron.js";
13
13
  import { Loop, LoopCommandInput, loopContract } from "./contract.js";
14
14
  /** The rules the model follows; the describe page and a live loop's prompt share them. */
15
- const rules = "You may start loops yourself; one live loop per Session. schedule_next is allowed only in the active self-paced loop's own run, clamped to 60–3600 seconds. Omitting it ends that loop. stop works in any Session run. Human pause/resume/stop and Esc remain user controls.";
15
+ const rules = "You may start loops yourself; one live loop per Session. Failed loop ticks retry with backoff; loop.get shows consecutiveFailures, lastError, and nextDueAt. schedule_next is allowed only in the active self-paced loop's own run, clamped to 60–3600 seconds. Omitting it ends that loop. stop works in any Session run. Human pause/resume/stop and Esc remain user controls.";
16
16
  /** Hashed prose and policy: every constant loop reads comes from here. */
17
17
  const config = {
18
18
  minDelaySeconds: 60,
@@ -38,6 +38,7 @@ const LoopRow = Schema.Struct({
38
38
  delaySeconds: Schema.Number,
39
39
  reason: Schema.String,
40
40
  })),
41
+ consecutiveFailures: Schema.Int.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0)), Schema.withDecodingDefaultKey(Effect.succeed(0))),
41
42
  });
42
43
  const collections = {
43
44
  loops: defineCollection(LoopRow, {
@@ -61,6 +62,7 @@ function projected(row) {
61
62
  reason: row.reason,
62
63
  nextDueAt: row.nextDueAt,
63
64
  lastError: row.lastError,
65
+ consecutiveFailures: row.consecutiveFailures,
64
66
  };
65
67
  }
66
68
  function commitLoop(ctx, row) {
@@ -127,6 +129,7 @@ function start(ctx, sessionId, input, cron, origin = "human") {
127
129
  lastError: null,
128
130
  startRequestId: input.requestId,
129
131
  requested: null,
132
+ consecutiveFailures: 0,
130
133
  };
131
134
  const stored = cron === null
132
135
  ? row
@@ -150,6 +153,8 @@ function requireLoop(ctx, sessionId) {
150
153
  }
151
154
  /** Where a resumed loop picks up: its cadence, or its recorded next instant. */
152
155
  function resumeAt(loop, now) {
156
+ if (loop.consecutiveFailures > 0)
157
+ return Effect.succeed(loop.nextDueAt === null ? null : Math.max(now, loop.nextDueAt));
153
158
  return loop.cron === null
154
159
  ? Effect.succeed(loop.nextDueAt)
155
160
  : nextOccurrence(loop.cron, now);
@@ -165,6 +170,27 @@ function settle(ctx, run) {
165
170
  return;
166
171
  if (run.workId !== loop.commandId)
167
172
  return;
173
+ if (run.outcome === "failed") {
174
+ const consecutiveFailures = Math.min(Number.MAX_SAFE_INTEGER, loop.consecutiveFailures + 1);
175
+ const backoffExponent = Math.min(consecutiveFailures - 1, 5);
176
+ const delayMs = Math.min(15 * 60_000, 30_000 * 2 ** backoffExponent);
177
+ const now = yield* Clock.currentTimeMillis;
178
+ const dueAt = now + delayMs;
179
+ const retry = {
180
+ ...loop,
181
+ state: loop.state,
182
+ requested: null,
183
+ reason: null,
184
+ nextDueAt: dueAt,
185
+ lastError: "Loop work ended with outcome failed.",
186
+ consecutiveFailures,
187
+ };
188
+ if (loop.state === "paused")
189
+ yield* commitLoop(ctx, retry);
190
+ else
191
+ yield* arm(ctx, retry, dueAt);
192
+ return yield* ctx.observe(loop.sessionId);
193
+ }
168
194
  if (run.outcome !== "completed" || run.terminal !== "completed") {
169
195
  yield* ctx.timers.cancel(loop.sessionId);
170
196
  yield* commitLoop(ctx, {
@@ -176,12 +202,22 @@ function settle(ctx, run) {
176
202
  lastError: run.outcome === "completed"
177
203
  ? "Completed loop work has no exact terminal."
178
204
  : `Loop work ended with outcome ${run.outcome}.`,
205
+ consecutiveFailures: loop.consecutiveFailures,
179
206
  });
180
207
  return yield* ctx.observe(loop.sessionId);
181
208
  }
182
209
  // A fixed interval loop's cadence is armed independently of its runs.
183
- if (loop.mode === "fixed_interval")
210
+ if (loop.mode === "fixed_interval") {
211
+ if (loop.consecutiveFailures !== 0 || loop.lastError !== null) {
212
+ yield* commitLoop(ctx, {
213
+ ...loop,
214
+ consecutiveFailures: 0,
215
+ lastError: null,
216
+ });
217
+ yield* ctx.observe(loop.sessionId);
218
+ }
184
219
  return;
220
+ }
185
221
  // Omitting schedule_next during the run ends a self-paced loop.
186
222
  if (loop.requested?.workId !== run.workId) {
187
223
  yield* commitLoop(ctx, {
@@ -189,6 +225,8 @@ function settle(ctx, run) {
189
225
  state: "finished",
190
226
  reason: "no next wake requested",
191
227
  nextDueAt: null,
228
+ lastError: null,
229
+ consecutiveFailures: 0,
192
230
  });
193
231
  return yield* ctx.observe(loop.sessionId);
194
232
  }
@@ -198,6 +236,8 @@ function settle(ctx, run) {
198
236
  reason: loop.requested.reason,
199
237
  requested: null,
200
238
  nextDueAt: now + loop.requested.delaySeconds * 1_000,
239
+ lastError: null,
240
+ consecutiveFailures: 0,
201
241
  };
202
242
  // A paused loop keeps the instant; resume arms it.
203
243
  if (loop.state === "paused")
@@ -273,17 +313,24 @@ function fire(ctx, sessionId, dueAt, now) {
273
313
  yield* ctx.observe(sessionId);
274
314
  });
275
315
  }
276
- /** Run one tool transaction and surface its failure to the model as an Error. */
277
- function toolCall(ctx, body) {
316
+ /** Run one tool transaction and preserve its owner-admitted diagnostic. */
317
+ function toolCall(ctx, operation, body) {
278
318
  return Effect.runPromiseExit(ctx.transact(body)).then((exit) => {
279
319
  if (Exit.isSuccess(exit))
280
320
  return exit.value;
281
321
  const failure = Cause.squash(exit.cause);
282
- return Promise.reject(new Error(failure instanceof ExtensionStop
283
- ? failure.reason
284
- : failure instanceof Error
285
- ? failure.message
286
- : "Loop tool failed"));
322
+ return Promise.reject(new CapabilityFailed({
323
+ namespace: "loop",
324
+ operation,
325
+ cause: {
326
+ kind: "OperationFailed",
327
+ message: failure instanceof ExtensionStop
328
+ ? failure.reason
329
+ : failure instanceof Error
330
+ ? failure.message
331
+ : "Loop tool failed",
332
+ },
333
+ }));
287
334
  });
288
335
  }
289
336
  // Mycelium's maintained operation interface takes Zod for model-tool I/O.
@@ -305,6 +352,7 @@ const toolLoop = z.strictObject({
305
352
  reason: z.string().nullable(),
306
353
  nextDueAt: z.number().nullable(),
307
354
  lastError: z.string().nullable(),
355
+ consecutiveFailures: z.number().int().nonnegative(),
308
356
  });
309
357
  /** The self-paced `schedule_next` tool for exactly the loop's current run. */
310
358
  function loopModule(ctx, sessionId, workId) {
@@ -318,7 +366,7 @@ function loopModule(ctx, sessionId, workId) {
318
366
  description: "Get the current Session loop and its next scheduled wake.",
319
367
  input: z.strictObject({}),
320
368
  output: toolLoop.nullable(),
321
- execute: () => toolCall(ctx, ctx.state.loops
369
+ execute: () => toolCall(ctx, "get", ctx.state.loops
322
370
  .get(sessionId)
323
371
  .pipe(Effect.map((found) => Option.isSome(found) ? projected(found.value) : null))),
324
372
  }),
@@ -343,7 +391,7 @@ function loopModule(ctx, sessionId, workId) {
343
391
  ]),
344
392
  }),
345
393
  output: toolLoop,
346
- execute: ({ prompt, cadence }) => toolCall(ctx, Effect.gen(function* () {
394
+ execute: ({ prompt, cadence }) => toolCall(ctx, "start", Effect.gen(function* () {
347
395
  const requestId = yield* freshRequestId;
348
396
  const cron = cadence.kind === "self_paced"
349
397
  ? null
@@ -362,7 +410,7 @@ function loopModule(ctx, sessionId, workId) {
362
410
  description: "Stop this Session's loop from any run, with a visible reason. Cancel its next scheduled wake.",
363
411
  input: z.strictObject({ reason: z.string().trim().min(1).max(1_000) }),
364
412
  output: toolLoop,
365
- execute: ({ reason }) => toolCall(ctx, stopLoop(ctx, sessionId, reason)),
413
+ execute: ({ reason }) => toolCall(ctx, "stop", stopLoop(ctx, sessionId, reason)),
366
414
  }),
367
415
  schedule_next: Mycelium.operation({
368
416
  replay: "unsafe",
@@ -373,13 +421,16 @@ function loopModule(ctx, sessionId, workId) {
373
421
  dueAt: z.number(),
374
422
  reason: z.string().min(1).max(4_000),
375
423
  }),
376
- execute: ({ delaySeconds, reason }) => toolCall(ctx, Effect.gen(function* () {
424
+ execute: ({ delaySeconds, reason }) => toolCall(ctx, "schedule_next", Effect.gen(function* () {
377
425
  const found = yield* ctx.state.loops.get(sessionId);
378
- if (Option.isNone(found) ||
379
- found.value.state !== "active" ||
426
+ if (Option.isNone(found))
427
+ return yield* Effect.fail(conflict("schedule_next only works inside this loop's own tick run"));
428
+ if (found.value.state === "failed")
429
+ return yield* Effect.fail(conflict("loop is failed; start a new one with loop.start"));
430
+ if (found.value.state !== "active" ||
380
431
  found.value.mode !== "self_paced" ||
381
432
  found.value.commandId !== workId)
382
- return yield* Effect.fail(conflict("Loop work guard is stale"));
433
+ return yield* Effect.fail(conflict("schedule_next only works inside this loop's own tick run"));
383
434
  const clamped = Math.max(ctx.config.minDelaySeconds, Math.min(ctx.config.maxDelaySeconds, delaySeconds));
384
435
  yield* commitLoop(ctx, {
385
436
  ...found.value,
@@ -575,10 +626,13 @@ export const loopCatalog = catalog([
575
626
  /** Durable Session loop implementation with prompts, controls and scheduled occurrences. */
576
627
  export const loopExtension = defineExtension({
577
628
  id: loopContract.id,
578
- version: "3.0.0",
629
+ version: "4.0.0",
579
630
  })
580
631
  .config(config)
581
- .state({ version: 3, migrations: {}, collections })
632
+ .state({
633
+ version: 3,
634
+ collections,
635
+ })
582
636
  .define({
583
637
  prompt: (ctx, { sessionId }) => ctx.state.loops.get(sessionId).pipe(Effect.map((found) => [
584
638
  {
@@ -35,9 +35,9 @@ export function createConversationAgents(storage) {
35
35
  function created(parentSessionId, agentId) {
36
36
  sql().exec("INSERT OR IGNORE INTO oyster_conversation_agents(parent_session_id, agent_id, session_id, at) VALUES (?, ?, NULL, ?)", parentSessionId, agentId, Date.now());
37
37
  }
38
- /** A send names the conversation to open and moves the row to the front. */
38
+ /** A send may not know its Session until the recipient accepts it. */
39
39
  function sent(parentSessionId, agentId, sessionId) {
40
- sql().exec("INSERT INTO oyster_conversation_agents(parent_session_id, agent_id, session_id, at) VALUES (?, ?, ?, ?) ON CONFLICT(parent_session_id, agent_id) DO UPDATE SET session_id = excluded.session_id, at = excluded.at", parentSessionId, agentId, sessionId, Date.now());
40
+ sql().exec("INSERT INTO oyster_conversation_agents(parent_session_id, agent_id, session_id, at) VALUES (?, ?, ?, ?) ON CONFLICT(parent_session_id, agent_id) DO UPDATE SET session_id = excluded.session_id, at = excluded.at", parentSessionId, agentId, sessionId ?? null, Date.now());
41
41
  }
42
42
  /** One conversation's directory: its accepted creates and sends are recorded. */
43
43
  function recording(parentSessionId, directory) {
@@ -55,6 +55,7 @@ const Loop: Struct<{
55
55
  reason: NullOr<String>;
56
56
  nextDueAt: NullOr<Int>;
57
57
  lastError: NullOr<String>;
58
+ consecutiveFailures: Int;
58
59
  }>;
59
60
  ```
60
61
 
@@ -109,6 +110,7 @@ const LoopProjection: Struct<{
109
110
  reason: NullOr<String>;
110
111
  nextDueAt: NullOr<Int>;
111
112
  lastError: NullOr<String>;
113
+ consecutiveFailures: Int;
112
114
  }>;
113
115
  }>;
114
116
  ```
@@ -127,33 +129,33 @@ Session-owned loop controls and their visible state.
127
129
 
128
130
  #### Type Declaration
129
131
 
130
- | Name | Type | Default value | Description |
131
- | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ---------------------------------------------------------- |
132
- | <a id="property-id"></a> `id` | `string` & `Brand`\<`"ExtensionId"`\> | - | Stable extension identity. |
133
- | <a id="property-commands"></a> `commands` | `object` | - | Loop operations admitted by the Session owner. |
134
- | `commands.loop` | `object` | - | Open or answer the human loop controls. |
135
- | `commands.loop.scope` | `"session"` | `"session"` | The Session owns this loop. |
136
- | `commands.loop.input` | `Union`\<readonly \[`Struct`\<\{ `action`: `Literal`\<`"start"`\>; `text`: `String`; `requestId`: `String`; \}\>, `Struct`\<\{ `action`: `Literals`\<readonly \[`"status"`, `"pause"`, `"resume"`, `"stop"`, `"new"`\]\>; \}\>, `Struct`\<\{ \}\>\]\> | `LoopCommandInput` | Decode the selected human action. |
137
- | `commands.loop.output` | `declare`\<`WhistleCatalogStepV1`, `WhistleCatalogStepV1`\> | `CatalogStep` | Return the server-owned conversation step. |
138
- | `commands.startSelfPaced` | `object` | - | Start a loop whose next occurrence is chosen by the model. |
139
- | `commands.startSelfPaced.scope` | `"session"` | `"session"` | The Session owns this loop. |
140
- | `commands.startSelfPaced.input` | `Struct`\<\{ `prompt`: `String`; `requestId`: `String`; \}\> | - | Decode the repeated prompt and command identity. |
141
- | `commands.startSelfPaced.output` | `Struct`\<\{ `loop`: `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; \}\>; `accepted`: `Struct`\<\{ `status`: `Literal`\<`"accepted"`\>; `commandId`: `String`; \}\>; \}\> | `StartResult` | Return the loop and admission receipt. |
142
- | `commands.startFixedInterval` | `object` | - | Start a loop with a fixed cron cadence. |
143
- | `commands.startFixedInterval.scope` | `"session"` | `"session"` | The Session owns this loop. |
144
- | `commands.startFixedInterval.input` | `Struct`\<\{ `expression`: `String`; `timeZone`: `String`; `prompt`: `String`; `requestId`: `String`; \}\> | - | Decode the prompt, cadence and command identity. |
145
- | `commands.startFixedInterval.output` | `Struct`\<\{ `loop`: `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; \}\>; `accepted`: `Struct`\<\{ `status`: `Literal`\<`"accepted"`\>; `commandId`: `String`; \}\>; \}\> | `StartResult` | Return the loop and admission receipt. |
146
- | `commands.pause` | `object` | - | Suspend the current loop. |
147
- | `commands.pause.scope` | `"session"` | `"session"` | The Session owns this loop. |
148
- | `commands.pause.input` | `Struct`\<\{ \}\> | - | No additional arguments are required. |
149
- | `commands.pause.output` | `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; \}\> | `Loop` | Return the paused loop. |
150
- | `commands.resume` | `object` | - | Resume the current paused loop. |
151
- | `commands.resume.scope` | `"session"` | `"session"` | The Session owns this loop. |
152
- | `commands.resume.input` | `Struct`\<\{ \}\> | - | No additional arguments are required. |
153
- | `commands.resume.output` | `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; \}\> | `Loop` | Return the resumed loop. |
154
- | `commands.stop` | `object` | - | Stop future occurrences of the current loop. |
155
- | `commands.stop.scope` | `"session"` | `"session"` | The Session owns this loop. |
156
- | `commands.stop.input` | `Struct`\<\{ \}\> | - | No additional arguments are required. |
157
- | `commands.stop.output` | `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; \}\> | `Loop` | Return the stopped loop. |
158
- | <a id="property-projection"></a> `projection` | `object` | - | Current loop visible to Session clients. |
159
- | `projection.schema` | `Struct`\<\{ `loop`: `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; \}\>; \}\> | `LoopProjection` | Decoder for projected loop state. |
132
+ | Name | Type | Default value | Description |
133
+ | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ---------------------------------------------------------- |
134
+ | <a id="property-id"></a> `id` | `string` & `Brand`\<`"ExtensionId"`\> | - | Stable extension identity. |
135
+ | <a id="property-commands"></a> `commands` | `object` | - | Loop operations admitted by the Session owner. |
136
+ | `commands.loop` | `object` | - | Open or answer the human loop controls. |
137
+ | `commands.loop.scope` | `"session"` | `"session"` | The Session owns this loop. |
138
+ | `commands.loop.input` | `Union`\<readonly \[`Struct`\<\{ `action`: `Literal`\<`"start"`\>; `text`: `String`; `requestId`: `String`; \}\>, `Struct`\<\{ `action`: `Literals`\<readonly \[`"status"`, `"pause"`, `"resume"`, `"stop"`, `"new"`\]\>; \}\>, `Struct`\<\{ \}\>\]\> | `LoopCommandInput` | Decode the selected human action. |
139
+ | `commands.loop.output` | `declare`\<`WhistleCatalogStepV1`, `WhistleCatalogStepV1`\> | `CatalogStep` | Return the server-owned conversation step. |
140
+ | `commands.startSelfPaced` | `object` | - | Start a loop whose next occurrence is chosen by the model. |
141
+ | `commands.startSelfPaced.scope` | `"session"` | `"session"` | The Session owns this loop. |
142
+ | `commands.startSelfPaced.input` | `Struct`\<\{ `prompt`: `String`; `requestId`: `String`; \}\> | - | Decode the repeated prompt and command identity. |
143
+ | `commands.startSelfPaced.output` | `Struct`\<\{ `loop`: `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; `consecutiveFailures`: `Int`; \}\>; `accepted`: `Struct`\<\{ `status`: `Literal`\<`"accepted"`\>; `commandId`: `String`; \}\>; \}\> | `StartResult` | Return the loop and admission receipt. |
144
+ | `commands.startFixedInterval` | `object` | - | Start a loop with a fixed cron cadence. |
145
+ | `commands.startFixedInterval.scope` | `"session"` | `"session"` | The Session owns this loop. |
146
+ | `commands.startFixedInterval.input` | `Struct`\<\{ `expression`: `String`; `timeZone`: `String`; `prompt`: `String`; `requestId`: `String`; \}\> | - | Decode the prompt, cadence and command identity. |
147
+ | `commands.startFixedInterval.output` | `Struct`\<\{ `loop`: `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; `consecutiveFailures`: `Int`; \}\>; `accepted`: `Struct`\<\{ `status`: `Literal`\<`"accepted"`\>; `commandId`: `String`; \}\>; \}\> | `StartResult` | Return the loop and admission receipt. |
148
+ | `commands.pause` | `object` | - | Suspend the current loop. |
149
+ | `commands.pause.scope` | `"session"` | `"session"` | The Session owns this loop. |
150
+ | `commands.pause.input` | `Struct`\<\{ \}\> | - | No additional arguments are required. |
151
+ | `commands.pause.output` | `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; `consecutiveFailures`: `Int`; \}\> | `Loop` | Return the paused loop. |
152
+ | `commands.resume` | `object` | - | Resume the current paused loop. |
153
+ | `commands.resume.scope` | `"session"` | `"session"` | The Session owns this loop. |
154
+ | `commands.resume.input` | `Struct`\<\{ \}\> | - | No additional arguments are required. |
155
+ | `commands.resume.output` | `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; `consecutiveFailures`: `Int`; \}\> | `Loop` | Return the resumed loop. |
156
+ | `commands.stop` | `object` | - | Stop future occurrences of the current loop. |
157
+ | `commands.stop.scope` | `"session"` | `"session"` | The Session owns this loop. |
158
+ | `commands.stop.input` | `Struct`\<\{ \}\> | - | No additional arguments are required. |
159
+ | `commands.stop.output` | `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; `consecutiveFailures`: `Int`; \}\> | `Loop` | Return the stopped loop. |
160
+ | <a id="property-projection"></a> `projection` | `object` | - | Current loop visible to Session clients. |
161
+ | `projection.schema` | `Struct`\<\{ `loop`: `Struct`\<\{ `loopId`: `String`; `origin`: `Literals`\<readonly \[`"human"`, `"model"`\]\>; `expiresAt`: `NullOr`\<`Int`\>; `prompt`: `String`; `mode`: `Literals`\<readonly \[`"self_paced"`, `"fixed_interval"`\]\>; `cron`: `NullOr`\<`Struct`\<\{ `expression`: `String`; `timeZone`: `String`; \}\>\>; `state`: `Literals`\<readonly \[`"active"`, `"paused"`, `"finished"`, `"failed"`, `"cancelled"`\]\>; `commandId`: `String`; `reason`: `NullOr`\<`String`\>; `nextDueAt`: `NullOr`\<`Int`\>; `lastError`: `NullOr`\<`String`\>; `consecutiveFailures`: `Int`; \}\>; \}\> | `LoopProjection` | Decoder for projected loop state. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/shiitake-extensions",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "private": false,
6
6
  "description": "Botanical's product Shiitake extensions: goal, loop and Almanac on the generic extension surface",
@@ -108,9 +108,9 @@
108
108
  "dependencies": {
109
109
  "@fungi.computer/almanac": "0.2.0",
110
110
  "@fungi.computer/cairn": "0.1.0",
111
- "@fungi.computer/knapsack": "0.2.0",
112
- "@fungi.computer/mycelium": "0.2.0",
113
- "@fungi.computer/shiitake": "0.3.0",
111
+ "@fungi.computer/knapsack": "0.3.0",
112
+ "@fungi.computer/mycelium": "0.2.2",
113
+ "@fungi.computer/shiitake": "0.4.0",
114
114
  "@fungi.computer/watchdog": "0.1.0",
115
115
  "@fungi.computer/whistle": "0.1.0",
116
116
  "effect": "4.0.2",