@frockbot/plugin-shell 0.3.19 → 0.3.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/plugin-shell",
3
- "version": "0.3.19",
3
+ "version": "0.3.21",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -52,42 +52,42 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@cordisjs/plugin-webui": "0.8.2",
55
- "@frockbot/agent-runtime": "0.3.19",
56
- "@frockbot/application-foundation": "0.3.19",
57
- "@frockbot/catalog-core": "0.3.19",
58
- "@frockbot/client-core": "0.3.19",
59
- "@frockbot/client-ui": "0.3.19",
60
- "@frockbot/computer-core": "0.3.19",
61
- "@frockbot/computer-host-protocol": "0.3.19",
62
- "@frockbot/configuration-core": "0.3.19",
63
- "@frockbot/connection-core": "0.3.19",
64
- "@frockbot/kernel-agent-loop": "0.3.19",
65
- "@frockbot/kernel-composition": "0.3.19",
66
- "@frockbot/kernel-contracts": "0.3.19",
67
- "@frockbot/kernel-do": "0.3.19",
68
- "@frockbot/machine-protocol": "0.3.19",
69
- "@frockbot/plugin-applets": "0.3.19",
70
- "@frockbot/plugin-authoring": "0.3.19",
71
- "@frockbot/plugin-bot-template": "0.3.19",
72
- "@frockbot/plugin-computer": "0.3.19",
73
- "@frockbot/plugin-flock": "0.3.19",
74
- "@frockbot/plugin-image": "0.3.19",
75
- "@frockbot/plugin-machine-messages": "0.3.19",
76
- "@frockbot/plugin-mcp": "0.3.19",
77
- "@frockbot/plugin-memory": "0.3.19",
78
- "@frockbot/plugin-package-catalog": "0.3.19",
79
- "@frockbot/plugin-routines": "0.3.19",
80
- "@frockbot/plugin-skills": "0.3.19",
81
- "@frockbot/plugin-subagents": "0.3.19",
82
- "@frockbot/plugin-user-machine": "0.3.19",
83
- "@frockbot/protocol": "0.3.19",
55
+ "@frockbot/agent-runtime": "0.3.21",
56
+ "@frockbot/application-foundation": "0.3.21",
57
+ "@frockbot/catalog-core": "0.3.21",
58
+ "@frockbot/client-core": "0.3.21",
59
+ "@frockbot/client-ui": "0.3.21",
60
+ "@frockbot/computer-core": "0.3.21",
61
+ "@frockbot/computer-host-protocol": "0.3.21",
62
+ "@frockbot/configuration-core": "0.3.21",
63
+ "@frockbot/connection-core": "0.3.21",
64
+ "@frockbot/kernel-agent-loop": "0.3.21",
65
+ "@frockbot/kernel-composition": "0.3.21",
66
+ "@frockbot/kernel-contracts": "0.3.21",
67
+ "@frockbot/kernel-do": "0.3.21",
68
+ "@frockbot/machine-protocol": "0.3.21",
69
+ "@frockbot/plugin-applets": "0.3.21",
70
+ "@frockbot/plugin-authoring": "0.3.21",
71
+ "@frockbot/plugin-bot-template": "0.3.21",
72
+ "@frockbot/plugin-computer": "0.3.21",
73
+ "@frockbot/plugin-flock": "0.3.21",
74
+ "@frockbot/plugin-image": "0.3.21",
75
+ "@frockbot/plugin-machine-messages": "0.3.21",
76
+ "@frockbot/plugin-mcp": "0.3.21",
77
+ "@frockbot/plugin-memory": "0.3.21",
78
+ "@frockbot/plugin-package-catalog": "0.3.21",
79
+ "@frockbot/plugin-routines": "0.3.21",
80
+ "@frockbot/plugin-skills": "0.3.21",
81
+ "@frockbot/plugin-subagents": "0.3.21",
82
+ "@frockbot/plugin-user-machine": "0.3.21",
83
+ "@frockbot/protocol": "0.3.21",
84
84
  "cordis": "4.0.0-rc.8",
85
85
  "vue": "3.5.41"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@cloudflare/workers-types": "latest",
89
- "@frockbot/plugin-prompt": "0.3.19",
90
- "@frockbot/plugin-tools": "0.3.19",
89
+ "@frockbot/plugin-prompt": "0.3.21",
90
+ "@frockbot/plugin-tools": "0.3.21",
91
91
  "@types/bun": "1.3.6",
92
92
  "@types/node": "26.2.0",
93
93
  "@vitejs/plugin-vue": "6.0.8",
package/src/agent.test.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  type ToolCall,
8
8
  type ToolExecutionContext,
9
9
  type TurnTypeV1,
10
+ TURN_DEADLINE_MS_V1,
10
11
  } from "@frockbot/kernel-contracts";
11
12
  import { SystemPromptRegistry } from "@frockbot/plugin-prompt";
12
13
  import { ToolRegistry } from "@frockbot/plugin-tools";
@@ -19,6 +20,7 @@ import {
19
20
  PARENT_HANDOFF_CAPABILITY_V1,
20
21
  SEND_MESSAGE_ALIAS_V1,
21
22
  SEND_TO_USER_TOOL_V1,
23
+ TIME_BUDGET_WARNING_MS_V1,
22
24
  USER_VOICE_CAPABILITY_V1,
23
25
  WAKE_PARENT_TOOL_V1,
24
26
  } from "./agent.ts";
@@ -575,6 +577,36 @@ describe("the acknowledgement reaches the user", () => {
575
577
  }
576
578
  });
577
579
 
580
+ test("the last two minutes of a Turn tell the model to send a status", async () => {
581
+ const mounted = await mount();
582
+ try {
583
+ const assemble = (remainingMs: number) =>
584
+ mounted.root.systemPrompt.assemble({
585
+ sessionId: "session-1",
586
+ provider: "provider-1",
587
+ model: "model-1",
588
+ turnType: "chat",
589
+ step: { current: 61, max: 64 },
590
+ deadline: {
591
+ at: TURN_DEADLINE_MS_V1,
592
+ now: TURN_DEADLINE_MS_V1 - remainingMs,
593
+ },
594
+ });
595
+ const boundary = await assemble(TIME_BUDGET_WARNING_MS_V1);
596
+ expect(boundary.text).not.toContain("<time_budget>");
597
+ const warning = await assemble(TIME_BUDGET_WARNING_MS_V1 - 1);
598
+ expect(warning.text).toContain("<time_budget>");
599
+ expect(warning.text).toContain("fewer than 2 minutes left");
600
+ expect(warning.text).toContain("Do not start new work");
601
+ expect(warning.text).toContain(SEND_TO_USER_TOOL_V1);
602
+ expect(warning.text.indexOf("<time_budget>")).toBeGreaterThan(
603
+ warning.text.indexOf("<step_budget>"),
604
+ );
605
+ } finally {
606
+ await mounted.dispose();
607
+ }
608
+ });
609
+
578
610
  test("a step that already spoke is left alone, and a replay adds nothing", async () => {
579
611
  const mounted = await mount();
580
612
  try {
package/src/agent.ts CHANGED
@@ -234,6 +234,11 @@ export const STEP_BUDGET_PROMPT_SECTION_V1 = "step-budget";
234
234
  export const STEP_BUDGET_PROMPT_ORDER_V1 = 90;
235
235
  /** The section appears when this many steps or fewer remain after this one. */
236
236
  export const STEP_BUDGET_WARNING_STEPS_V1 = 3;
237
+ export const TIME_BUDGET_PROMPT_SECTION_V1 = "time-budget";
238
+ /** Immediately after the step warning, so the time warning is read last. */
239
+ export const TIME_BUDGET_PROMPT_ORDER_V1 = 91;
240
+ /** The section appears only once strictly fewer than two minutes remain. */
241
+ export const TIME_BUDGET_WARNING_MS_V1 = 2 * 60_000;
237
242
 
238
243
  /**
239
244
  * What the model is told when its reply is about to be stopped.
@@ -265,6 +270,23 @@ export function stepBudgetPromptTextV1(context: {
265
270
  ].join("\n");
266
271
  }
267
272
 
273
+ /** Warns by wall-clock budget even when the model has used few steps. */
274
+ export function timeBudgetPromptTextV1(context: {
275
+ deadline?: { at: number; now: number };
276
+ }): string {
277
+ const deadline = context.deadline;
278
+ if (!deadline) return "";
279
+ const remaining = deadline.at - deadline.now;
280
+ if (!Number.isFinite(remaining) || remaining >= TIME_BUDGET_WARNING_MS_V1) {
281
+ return "";
282
+ }
283
+ return [
284
+ "<time_budget>",
285
+ `This Turn has fewer than 2 minutes left before it is stopped automatically. Do not start new work. Call \`${SEND_TO_USER_TOOL_V1}\` now with a short status for the person: what is finished, what is not, and what they can do next.`,
286
+ "</time_budget>",
287
+ ].join("\n");
288
+ }
289
+
268
290
  export const CONVERSATION_PROMPT_TEXT_V1 = [
269
291
  "## Talking to the user",
270
292
  "",
@@ -469,6 +491,11 @@ export const shellAgentPlugin: Plugin.Function = (ctx) => {
469
491
  order: STEP_BUDGET_PROMPT_ORDER_V1,
470
492
  render: (context) => stepBudgetPromptTextV1(context),
471
493
  }),
494
+ ctx.systemPrompt.register({
495
+ id: TIME_BUDGET_PROMPT_SECTION_V1,
496
+ order: TIME_BUDGET_PROMPT_ORDER_V1,
497
+ render: (context) => timeBudgetPromptTextV1(context),
498
+ }),
472
499
  ctx.tools.register(
473
500
  createSendToUserTool(SEND_TO_USER_TOOL_V1, ctx.sessions),
474
501
  userVoice ? { admissionCeiling: userVoice } : undefined,
@@ -499,9 +499,19 @@
499
499
  background: var(--frock-action-primary);
500
500
  }
501
501
 
502
+ /*
503
+ * The User's bubble and its "via" line share a column anchored at the end.
504
+ * It spans the whole thread: the bubble's max-width is a percentage, and
505
+ * against a wrapper that shrink-wraps the bubble that percentage resolves to
506
+ * less than the bubble itself, which is how "Hi" came out one letter per
507
+ * line (2026-09-05).
508
+ */
502
509
  .message-user-column {
503
- display: grid;
504
- justify-items: end;
510
+ display: flex;
511
+ width: 100%;
512
+ min-width: 0;
513
+ flex-direction: column;
514
+ align-items: flex-end;
505
515
  gap: 4px;
506
516
  }
507
517