deepclause-sdk 0.2.2 → 0.4.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.
Files changed (117) hide show
  1. package/README.md +86 -3
  2. package/dist/agent.d.ts +17 -0
  3. package/dist/agent.d.ts.map +1 -1
  4. package/dist/agent.js +148 -42
  5. package/dist/agent.js.map +1 -1
  6. package/dist/cli/config.d.ts +12 -12
  7. package/dist/cli/repl.d.ts +13 -1
  8. package/dist/cli/repl.d.ts.map +1 -1
  9. package/dist/cli/repl.js +177 -5
  10. package/dist/cli/repl.js.map +1 -1
  11. package/dist/cli/run.d.ts.map +1 -1
  12. package/dist/cli/run.js +4 -0
  13. package/dist/cli/run.js.map +1 -1
  14. package/dist/forms/index.d.ts +3 -0
  15. package/dist/forms/index.d.ts.map +1 -0
  16. package/dist/forms/index.js +3 -0
  17. package/dist/forms/index.js.map +1 -0
  18. package/dist/forms/registry.d.ts +119 -0
  19. package/dist/forms/registry.d.ts.map +1 -0
  20. package/dist/forms/registry.js +256 -0
  21. package/dist/forms/registry.js.map +1 -0
  22. package/dist/forms/spec.d.ts +193 -0
  23. package/dist/forms/spec.d.ts.map +1 -0
  24. package/dist/forms/spec.js +295 -0
  25. package/dist/forms/spec.js.map +1 -0
  26. package/dist/index.d.ts +7 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +7 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/prolog/loader.d.ts +18 -0
  31. package/dist/prolog/loader.d.ts.map +1 -1
  32. package/dist/prolog/loader.js +26 -1
  33. package/dist/prolog/loader.js.map +1 -1
  34. package/dist/prolog-src/deepclause_mi.pl +786 -40
  35. package/dist/repl/agent.d.ts +11 -0
  36. package/dist/repl/agent.d.ts.map +1 -1
  37. package/dist/repl/agent.js +23 -9
  38. package/dist/repl/agent.js.map +1 -1
  39. package/dist/repl/form-widget.d.ts +56 -0
  40. package/dist/repl/form-widget.d.ts.map +1 -0
  41. package/dist/repl/form-widget.js +289 -0
  42. package/dist/repl/form-widget.js.map +1 -0
  43. package/dist/repl/harness-tools.d.ts +4 -1
  44. package/dist/repl/harness-tools.d.ts.map +1 -1
  45. package/dist/repl/harness-tools.js +15 -58
  46. package/dist/repl/harness-tools.js.map +1 -1
  47. package/dist/repl/history.d.ts +24 -1
  48. package/dist/repl/history.d.ts.map +1 -1
  49. package/dist/repl/history.js +76 -8
  50. package/dist/repl/history.js.map +1 -1
  51. package/dist/repl/index.d.ts +20 -2
  52. package/dist/repl/index.d.ts.map +1 -1
  53. package/dist/repl/index.js +9 -0
  54. package/dist/repl/index.js.map +1 -1
  55. package/dist/repl/process-backend.d.ts +2 -2
  56. package/dist/repl/process-backend.d.ts.map +1 -1
  57. package/dist/repl/process-executor.d.ts +11 -2
  58. package/dist/repl/process-executor.d.ts.map +1 -1
  59. package/dist/repl/process-executor.js +67 -18
  60. package/dist/repl/process-executor.js.map +1 -1
  61. package/dist/repl/process-worker.d.ts +35 -4
  62. package/dist/repl/process-worker.d.ts.map +1 -1
  63. package/dist/repl/process-worker.js +78 -38
  64. package/dist/repl/process-worker.js.map +1 -1
  65. package/dist/repl/processes.d.ts +4 -0
  66. package/dist/repl/processes.d.ts.map +1 -1
  67. package/dist/repl/processes.js +20 -0
  68. package/dist/repl/processes.js.map +1 -1
  69. package/dist/repl/render.d.ts +17 -0
  70. package/dist/repl/render.d.ts.map +1 -1
  71. package/dist/repl/render.js +60 -0
  72. package/dist/repl/render.js.map +1 -1
  73. package/dist/repl/session.d.ts +143 -7
  74. package/dist/repl/session.d.ts.map +1 -1
  75. package/dist/repl/session.js +317 -20
  76. package/dist/repl/session.js.map +1 -1
  77. package/dist/repl/tui/form-view.d.ts +17 -0
  78. package/dist/repl/tui/form-view.d.ts.map +1 -0
  79. package/dist/repl/tui/form-view.js +25 -0
  80. package/dist/repl/tui/form-view.js.map +1 -0
  81. package/dist/repl/tui/renderer.d.ts +4 -0
  82. package/dist/repl/tui/renderer.d.ts.map +1 -1
  83. package/dist/repl/tui/renderer.js +12 -0
  84. package/dist/repl/tui/renderer.js.map +1 -1
  85. package/dist/repl/worker-host.d.ts +16 -2
  86. package/dist/repl/worker-host.d.ts.map +1 -1
  87. package/dist/repl/worker-host.js +38 -6
  88. package/dist/repl/worker-host.js.map +1 -1
  89. package/dist/repl/worker-protocol.d.ts +14 -0
  90. package/dist/repl/worker-protocol.d.ts.map +1 -1
  91. package/dist/repl/worker-protocol.js +8 -1
  92. package/dist/repl/worker-protocol.js.map +1 -1
  93. package/dist/runner.d.ts +69 -1
  94. package/dist/runner.d.ts.map +1 -1
  95. package/dist/runner.js +381 -44
  96. package/dist/runner.js.map +1 -1
  97. package/dist/sdk.d.ts.map +1 -1
  98. package/dist/sdk.js +12 -0
  99. package/dist/sdk.js.map +1 -1
  100. package/dist/system/assets/docs/AGENT_PROMPT.md +452 -18
  101. package/dist/system/assets/docs/CHAT_PROMPT.md +28 -0
  102. package/dist/system/assets/docs/DML_AUTHORING.md +8 -4
  103. package/dist/system/assets/docs/DML_FORMS.md +740 -0
  104. package/dist/system/assets/docs/DML_REFERENCE.md +239 -4
  105. package/dist/system/assets/docs/HARNESS_EXAMPLES.md +94 -0
  106. package/dist/system/assets/index.d.ts +2 -2
  107. package/dist/system/assets/index.d.ts.map +1 -1
  108. package/dist/system/assets/index.js +4 -0
  109. package/dist/system/assets/index.js.map +1 -1
  110. package/dist/system/environment.d.ts +6 -0
  111. package/dist/system/environment.d.ts.map +1 -1
  112. package/dist/system/environment.js +5 -4
  113. package/dist/system/environment.js.map +1 -1
  114. package/dist/types.d.ts +48 -1
  115. package/dist/types.d.ts.map +1 -1
  116. package/package.json +4 -2
  117. package/src/prolog-src/deepclause_mi.pl +786 -40
package/README.md CHANGED
@@ -329,6 +329,20 @@ deepclause repl --session last # resume the most recent session
329
329
  deepclause repl --session repl_m9x1_1 # …or one by id
330
330
  ```
331
331
 
332
+ A turn can also **suspend itself** at a safe point and be continued on resume —
333
+ the continuation is a goal, so the program says where it is safe:
334
+
335
+ ```prolog
336
+ loop(N) :-
337
+ ( N >= 2 -> suspend(loop(N)) % stop here; resume with loop(N)
338
+ ; M is N + 1, loop(M)
339
+ ).
340
+ ```
341
+
342
+ `suspend/1` refuses while the turn holds something a snapshot cannot carry — an
343
+ open stream or an open form — so a resumed session never inherits a half-written
344
+ file. See [`docs/DML_REPL_SUSPEND_RESUME.md`](docs/DML_REPL_SUSPEND_RESUME.md).
345
+
332
346
  ### Why Prolog/DML for a self-modifying harness
333
347
 
334
348
  An RLM-style harness works best when the harness *is* the program the model
@@ -411,9 +425,10 @@ dml> /ps --watch --logs
411
425
  debater › finished
412
426
  ```
413
427
 
414
- The authoring agent uses the same machinery through its `spawn` / `ps` / `send`
415
- / `call` / `stop` / `swarm` / `wait` tools, and DML programs use the built-ins
416
- directly (`spawn`, `send`/`receive`, `serve`/`service_call`, `swarm`/`pmap`). A
428
+ The authoring agent uses the same machinery through its `ps` / `send` / `stop` /
429
+ `swarm` / `wait` tools, and drives `spawn` and request/reply through `execute`
430
+ with the DML built-ins (`spawn`, `send`/`receive`, `serve`/`service_call`,
431
+ `swarm`/`pmap`). A
417
432
  subagent runs against a **private snapshot** of the module with **value
418
433
  semantics** — a result comes back by message, never by a shared variable — which
419
434
  is what makes it safe to run many at once.
@@ -461,6 +476,66 @@ only for models that support it.
461
476
  Credentials live in pi's agent dir (`~/.pi/agent`) unless `backend.agentDir`
462
477
  overrides it. Existing pi installations stay untouched.
463
478
 
479
+ ### Local models (Ollama, llama.cpp, vLLM)
480
+
481
+ The default `pi` backend reads pi's `models.json`, which can declare **custom
482
+ OpenAI-compatible providers**, so a local server needs no SDK changes:
483
+
484
+ ```jsonc
485
+ // ~/.pi/agent/models.json
486
+ {
487
+ "providers": {
488
+ "loc": {
489
+ "name": "Local server",
490
+ "baseUrl": "http://localhost:11434/v1", // Ollama; llama.cpp: http://127.0.0.1:8080/v1
491
+ "apiKey": "ollama", // any string; satisfies the auth check
492
+ "api": "openai-completions", // not openai-responses
493
+ "models": [{
494
+ "id": "llama3.1",
495
+ "contextWindow": 131072,
496
+ "maxTokens": 8192,
497
+ "reasoning": false,
498
+ "input": ["text"],
499
+ "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 }
500
+ }]
501
+ }
502
+ }
503
+ }
504
+ ```
505
+
506
+ ```bash
507
+ deepclause set-backend pi --list # lists "loc (Local server) — configured [api-key]"
508
+ # and the model "loc/lecho"
509
+ deepclause repl --model loc:llama3.1
510
+ ```
511
+
512
+ `run` has no `--model` flag; set `backend.model` instead. DML can route a single
513
+ step there as well — `with_model("loc:llama3.1", Goal)`.
514
+
515
+ Point at a non-default file with `backend.modelsPath` in
516
+ `.deepclause/config.json` (or `PI_CODING_AGENT_DIR` to move pi's whole agent
517
+ dir), which keeps a project's catalog in the repository rather than the home
518
+ directory:
519
+
520
+ ```json
521
+ { "backend": { "kind": "pi", "model": "loc:llama3.1", "modelsPath": "./agent/models.json" } }
522
+ ```
523
+
524
+ - **`api` picks the wire protocol.** `openai-completions` is what Ollama,
525
+ llama.cpp, vLLM and SGLang serve; `openai-responses` is the newer OpenAI API.
526
+ Per-provider `compat` flags cover servers that differ
527
+ (`maxTokensField`, `supportsUsageInStreaming`, `requiresToolResultName`, …),
528
+ and a model's `samplingParams` forwards `top_p`/`top_k`/`repetition_penalty`.
529
+ - **Tool calling is required for `task/N`**, which ends by calling
530
+ `set_result`/`finish`; a text-only model exhausts the turn instead. `chat/1`
531
+ needs no tools, so a small local model is usable there, or pick a model whose
532
+ chat template supports tools.
533
+ - **`list-models` prints the bundled model database** and will not show custom
534
+ entries; `set-backend pi --list` prints pi's catalog, which does.
535
+ - The `env` backend can also point at a local server
536
+ (`providers.openai.baseUrl` + `apiKey`), but it always speaks chat completions
537
+ and **`repl` ignores `providers.*.baseUrl`** today — prefer the `pi` backend.
538
+
464
539
  ## run
465
540
 
466
541
  `run` uses the **same engine as the REPL**: it opens a module, loads the
@@ -662,6 +737,13 @@ npm test # vitest
662
737
  npm run repl # deepclause repl via tsx
663
738
  ```
664
739
 
740
+ **`npm run build` is required for the default process backend.** L2 (worker)
741
+ processes run `dist/repl/process-worker.js`, so editing `src` without rebuilding
742
+ runs old worker code against a new main — the symptom used to be child sessions
743
+ that silently had no harness. `WORKER_PROTOCOL_VERSION` now fails that loudly
744
+ (`stale L2 process worker: … run 'npm run build'`). The tests that need it
745
+ (`repl-process-worker-child`) skip themselves when `dist` is absent.
746
+
665
747
  See [`docs/`](docs/): [`SUBAGENTS.md`](docs/SUBAGENTS.md) (subagents and
666
748
  concurrency), [`STATUS.md`](docs/STATUS.md) (current state),
667
749
  [`REPL.md`](docs/REPL.md),
@@ -670,6 +752,7 @@ concurrency), [`STATUS.md`](docs/STATUS.md) (current state),
670
752
  [`DML_MEDIA.md`](docs/DML_MEDIA.md),
671
753
  [`DML_MODEL_SWITCHING.md`](docs/DML_MODEL_SWITCHING.md),
672
754
  [`DML_REPL_WORKER_EXECUTION.md`](docs/DML_REPL_WORKER_EXECUTION.md) (plan),
755
+ [`DML_FORMS.md`](docs/DML_FORMS.md) (structured human input),
673
756
  [`JUDGMENT_BACKENDS.md`](docs/JUDGMENT_BACKENDS.md).
674
757
 
675
758
  ## License
package/dist/agent.d.ts CHANGED
@@ -11,6 +11,21 @@ import type { ExecutionEnvironment } from './system/runtime/shell-manager.js';
11
11
  export interface AgentLoopOptions {
12
12
  taskDescription: string;
13
13
  outputVars: (string | TypedVar)[];
14
+ /**
15
+ * Conversational mode: a chat turn, not a task.
16
+ *
17
+ * A traditional agent loop — tools, but no `finish`/`set_result`, and the turn
18
+ * ends when the model stops calling tools. That is how a chat agent works: it
19
+ * replies, and the next message starts the next turn. Task mode, by contrast,
20
+ * insists on an explicit `finish` and nudges the model when it does not.
21
+ */
22
+ conversational?: boolean;
23
+ /**
24
+ * A conversational turn has run out of tool calls. Return a message to keep the
25
+ * turn alive (the runner uses it to deliver a form the person is still filling
26
+ * in), or null to end the turn.
27
+ */
28
+ onIdleTurn?: () => Promise<string | null>;
14
29
  memory: MemoryMessage[];
15
30
  tools: Map<string, ToolDefinition>;
16
31
  /**
@@ -25,6 +40,8 @@ export interface AgentLoopOptions {
25
40
  * and dates in the dark.
26
41
  */
27
42
  environment?: ExecutionEnvironment;
43
+ /** Name of the single capability host tool (default `bash`; the browser uses `python`). */
44
+ capabilityTool?: string;
28
45
  modelOptions: {
29
46
  model: string;
30
47
  provider: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
package/dist/agent.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * - Uses Zod schemas for tool definitions
7
7
  * - Uses result.response.messages for message history management
8
8
  */
9
- import { generateText, streamText, hasToolCall, tool as aiTool } from 'ai';
9
+ import { generateText, streamText, hasToolCall, stepCountIs, tool as aiTool } from 'ai';
10
10
  import { z } from 'zod';
11
11
  import { createModelProvider } from './prolog/bridge.js';
12
12
  import { readSystemPromptAsset } from './system/assets/index.js';
@@ -512,7 +512,9 @@ async function runInjectedAgentLoop(options, normalizedOutputVars, combinedSyste
512
512
  success: false,
513
513
  };
514
514
  const requiredVarNames = normalizedOutputVars.map((variable) => variable.name);
515
- const backendTools = [{
515
+ // A chat turn has no `finish`/`set_result`: it ends when the model stops calling
516
+ // tools, like a traditional agent loop.
517
+ const backendTools = options.conversational ? [] : [{
516
518
  name: 'finish',
517
519
  description: 'Complete the task. success=true requires every requested result variable to be set.',
518
520
  parameters: {
@@ -575,7 +577,7 @@ async function runInjectedAgentLoop(options, normalizedOutputVars, combinedSyste
575
577
  const messages = [
576
578
  { role: 'system', content: combinedSystemPrompt },
577
579
  ...historyMessages,
578
- { role: 'user', content: `Subtask: ${options.taskDescription}` },
580
+ { role: 'user', content: options.conversational ? options.taskDescription : `Subtask: ${options.taskDescription}` },
579
581
  ];
580
582
  const maxIterations = Math.max(1, Number(process.env.DC_MAX_ITERATIONS) || 1000);
581
583
  for (let iteration = 0; iteration < maxIterations && !state.finished; iteration++) {
@@ -707,10 +709,24 @@ async function runInjectedAgentLoop(options, normalizedOutputVars, combinedSyste
707
709
  });
708
710
  }
709
711
  if (toolCalls.length === 0 && !state.finished) {
710
- messages.push({
711
- role: 'user',
712
- content: 'Continue the task. Use set_result for each requested output, then call finish.',
713
- });
712
+ if (options.conversational) {
713
+ // The reply is the answer — unless the host is still holding something
714
+ // for this turn (a form the person has not submitted yet).
715
+ const injected = options.onIdleTurn ? await options.onIdleTurn() : null;
716
+ if (injected) {
717
+ messages.push({ role: 'user', content: injected });
718
+ }
719
+ else {
720
+ state.finished = true;
721
+ state.success = true;
722
+ }
723
+ }
724
+ else {
725
+ messages.push({
726
+ role: 'user',
727
+ content: 'Continue the task. Use set_result for each requested output, then call finish.',
728
+ });
729
+ }
714
730
  }
715
731
  }
716
732
  return {
@@ -748,26 +764,28 @@ export async function runAgentLoop(options) {
748
764
  // Build the AI SDK tools using Zod schemas
749
765
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
750
766
  const aiTools = {};
751
- // Add finish tool with Zod schema
767
+ // Add finish tool with Zod schema. A chat turn has no finish: the model replies
768
+ // and stops, and the next message starts the next turn.
752
769
  const requiredVarNames = normalizedOutputVars.map(v => v.name);
753
- aiTools['finish'] = aiTool({
754
- description: 'CRITICAL: You MUST call this tool to complete the task and return success/failure. Call with success=true if you have set all required results, or success=false if the task is impossible.',
755
- inputSchema: z.object({
756
- success: z.boolean().describe('Whether the task was completed successfully')
757
- }),
758
- execute: async ({ success: s }) => {
759
- // Guard: success=true requires all output variables to be set
760
- if (s && requiredVarNames.length > 0) {
761
- const missing = requiredVarNames.filter(v => !(v in variables));
762
- if (missing.length > 0) {
763
- return { finished: false, error: `Cannot finish with success=true — missing required result variable(s): ${missing.join(', ')}. Call set_result for each before finishing.` };
770
+ if (!options.conversational)
771
+ aiTools['finish'] = aiTool({
772
+ description: 'CRITICAL: You MUST call this tool to complete the task and return success/failure. Call with success=true if you have set all required results, or success=false if the task is impossible.',
773
+ inputSchema: z.object({
774
+ success: z.boolean().describe('Whether the task was completed successfully')
775
+ }),
776
+ execute: async ({ success: s }) => {
777
+ // Guard: success=true requires all output variables to be set
778
+ if (s && requiredVarNames.length > 0) {
779
+ const missing = requiredVarNames.filter(v => !(v in variables));
780
+ if (missing.length > 0) {
781
+ return { finished: false, error: `Cannot finish with success=true — missing required result variable(s): ${missing.join(', ')}. Call set_result for each before finishing.` };
782
+ }
764
783
  }
765
- }
766
- finished = true;
767
- success = s;
768
- return { finished: true, success: s };
769
- },
770
- });
784
+ finished = true;
785
+ success = s;
786
+ return { finished: true, success: s };
787
+ },
788
+ });
771
789
  // Add set_result tool for output variables
772
790
  if (normalizedOutputVars.length > 0) {
773
791
  const varNames = normalizedOutputVars.map(v => v.name);
@@ -850,6 +868,8 @@ export async function runAgentLoop(options) {
850
868
  tools,
851
869
  workspacePath: options.workspacePath,
852
870
  environment: options.environment,
871
+ capabilityTool: options.capabilityTool,
872
+ conversational: options.conversational,
853
873
  });
854
874
  // Extract system context from memory (user-defined system() calls)
855
875
  const systemContext = memory
@@ -949,7 +969,7 @@ export async function runAgentLoop(options) {
949
969
  maxOutputTokens: modelOptions.maxOutputTokens,
950
970
  abortSignal: signal,
951
971
  providerOptions: modelOptions.providerOptions,
952
- stopWhen: hasToolCall('finish'),
972
+ stopWhen: options.conversational ? stepCountIs(1) : hasToolCall('finish'),
953
973
  onStepFinish: (step) => {
954
974
  debugLog(`Step finished: finishReason=${step.finishReason} toolCalls=${step.toolCalls?.length ?? 0}`);
955
975
  },
@@ -1122,10 +1142,28 @@ export async function runAgentLoop(options) {
1122
1142
  outputs.push('Agent loop detected repeated non-progressing responses and stopped early.');
1123
1143
  break;
1124
1144
  }
1145
+ // A chat turn ends when the model stops calling tools: that reply is the
1146
+ // answer, and the next message starts the next turn. Unless the host is
1147
+ // still holding something for this turn — a form the person has not
1148
+ // submitted yet — in which case its submission is injected and the loop
1149
+ // continues, so the turn never ends with an unanswered form.
1150
+ if (options.conversational && !finished && toolCallsThisIteration.length === 0) {
1151
+ const injected = options.onIdleTurn ? await options.onIdleTurn() : null;
1152
+ if (injected) {
1153
+ messages.push({ role: 'user', content: injected });
1154
+ }
1155
+ else {
1156
+ finished = true;
1157
+ success = true;
1158
+ }
1159
+ }
1125
1160
  // If no tool calls were made, nudge the model to act.
1126
1161
  // This handles 'stop', 'other', and any unexpected finish reason.
1127
- const recoveryPrompt = buildRecoveryPrompt(normalizedOutputVars, repeatedStallCount, toolCallsThisIteration.length === 0);
1128
- const continuationPrompt = !finished && getLastMessageRole(messages) === 'assistant'
1162
+ // A chat turn is never nudged: the model simply finished its reply.
1163
+ const recoveryPrompt = options.conversational
1164
+ ? null
1165
+ : buildRecoveryPrompt(normalizedOutputVars, repeatedStallCount, toolCallsThisIteration.length === 0);
1166
+ const continuationPrompt = !options.conversational && !finished && getLastMessageRole(messages) === 'assistant'
1129
1167
  ? buildAssistantContinuationPrompt(normalizedOutputVars)
1130
1168
  : null;
1131
1169
  const nextPrompt = recoveryPrompt ?? continuationPrompt;
@@ -1242,10 +1280,28 @@ export async function runAgentLoop(options) {
1242
1280
  outputs.push('Agent loop detected repeated non-progressing responses and stopped early.');
1243
1281
  break;
1244
1282
  }
1283
+ // A chat turn ends when the model stops calling tools: that reply is the
1284
+ // answer, and the next message starts the next turn. Unless the host is
1285
+ // still holding something for this turn — a form the person has not
1286
+ // submitted yet — in which case its submission is injected and the loop
1287
+ // continues, so the turn never ends with an unanswered form.
1288
+ if (options.conversational && !finished && toolCallsThisIteration.length === 0) {
1289
+ const injected = options.onIdleTurn ? await options.onIdleTurn() : null;
1290
+ if (injected) {
1291
+ messages.push({ role: 'user', content: injected });
1292
+ }
1293
+ else {
1294
+ finished = true;
1295
+ success = true;
1296
+ }
1297
+ }
1245
1298
  // If no tool calls were made, nudge the model to act.
1246
1299
  // This handles 'stop', 'other', and any unexpected finish reason.
1247
- const recoveryPrompt = buildRecoveryPrompt(normalizedOutputVars, repeatedStallCount, toolCallsThisIteration.length === 0);
1248
- const continuationPrompt = !finished && getLastMessageRole(messages) === 'assistant'
1300
+ // A chat turn is never nudged: the model simply finished its reply.
1301
+ const recoveryPrompt = options.conversational
1302
+ ? null
1303
+ : buildRecoveryPrompt(normalizedOutputVars, repeatedStallCount, toolCallsThisIteration.length === 0);
1304
+ const continuationPrompt = !options.conversational && !finished && getLastMessageRole(messages) === 'assistant'
1249
1305
  ? buildAssistantContinuationPrompt(normalizedOutputVars)
1250
1306
  : null;
1251
1307
  const nextPrompt = recoveryPrompt ?? continuationPrompt;
@@ -1298,11 +1354,36 @@ export async function runAgentLoop(options) {
1298
1354
  // Extract assistant text responses from the conversation
1299
1355
  const assistantTextResponses = [];
1300
1356
  for (const m of messages) {
1301
- if (m.role === 'assistant' && typeof m.content === 'string' && m.content.trim()) {
1302
- assistantTextResponses.push(m.content);
1303
- }
1357
+ // An AI-SDK assistant message carries its text as content *parts*
1358
+ // (`[{type:'text', text:'…'}]`), not a string. Skipping those dropped every
1359
+ // assistant reply from the memory a conversation hands to the next turn (and
1360
+ // left `chat/2` binding an empty string).
1361
+ if (m.role !== 'assistant')
1362
+ continue;
1363
+ const text = messageText(m.content);
1364
+ if (text.trim())
1365
+ assistantTextResponses.push(text);
1304
1366
  }
1305
1367
  // Add result to persistent messages
1368
+ if (options.conversational) {
1369
+ // A chat turn keeps the conversation, not a task summary: the person's
1370
+ // message and what the agent actually said. No extra summarising model call.
1371
+ return {
1372
+ success: true,
1373
+ outputs,
1374
+ variables,
1375
+ messages: [
1376
+ ...conversationHistory
1377
+ .filter((message) => message.role === 'user' || message.role === 'assistant')
1378
+ .map((message) => ({
1379
+ role: message.role,
1380
+ content: typeof message.content === 'string' ? message.content : JSON.stringify(message.content),
1381
+ })),
1382
+ { role: 'user', content: taskDescription },
1383
+ ...assistantTextResponses.map((text) => ({ role: 'assistant', content: text })),
1384
+ ],
1385
+ };
1386
+ }
1306
1387
  if (success && Object.keys(variables).length > 0) {
1307
1388
  const varSummary = Object.entries(variables)
1308
1389
  .map(([k, v]) => `${k}: ${v}`)
@@ -1347,6 +1428,18 @@ export async function runAgentLoop(options) {
1347
1428
  messages: persistentMessages,
1348
1429
  };
1349
1430
  }
1431
+ /** An assistant message's text: a string, or its `{type: 'text'}` content parts. */
1432
+ function messageText(content) {
1433
+ if (typeof content === 'string')
1434
+ return content;
1435
+ if (!Array.isArray(content))
1436
+ return '';
1437
+ return content
1438
+ .map((part) => (part && typeof part === 'object' && part.type === 'text'
1439
+ ? String(part.text ?? '')
1440
+ : ''))
1441
+ .join('');
1442
+ }
1350
1443
  function getResponseMessages(response) {
1351
1444
  if (!response || typeof response !== 'object') {
1352
1445
  return [];
@@ -1360,15 +1453,17 @@ function getResponseMessages(response) {
1360
1453
  async function buildSystemPrompt(options) {
1361
1454
  const toolDescriptions = [];
1362
1455
  const normalizedOutputVars = options.outputVars;
1363
- // Add finish tool description
1364
- toolDescriptions.push('- finish(success: boolean): Signal task completion. Call finish(true) when done successfully, or finish(false) if the task cannot be completed.');
1365
- // Add set_result tool if we have output variables
1366
- if (normalizedOutputVars.length > 0) {
1367
- const varList = normalizedOutputVars.map(v => {
1368
- const typeStr = v.type === 'array' && v.itemType ? `array<${v.itemType}>` : v.type;
1369
- return `"${v.name}" (${typeStr})`;
1370
- }).join(', ');
1371
- toolDescriptions.push(`- set_result(variable: string, value: any): Store a result value. Variable must be one of: ${varList}`);
1456
+ if (!options.conversational) {
1457
+ // Add finish tool description
1458
+ toolDescriptions.push('- finish(success: boolean): Signal task completion. Call finish(true) when done successfully, or finish(false) if the task cannot be completed.');
1459
+ // Add set_result tool if we have output variables
1460
+ if (normalizedOutputVars.length > 0) {
1461
+ const varList = normalizedOutputVars.map(v => {
1462
+ const typeStr = v.type === 'array' && v.itemType ? `array<${v.itemType}>` : v.type;
1463
+ return `"${v.name}" (${typeStr})`;
1464
+ }).join(', ');
1465
+ toolDescriptions.push(`- set_result(variable: string, value: any): Store a result value. Variable must be one of: ${varList}`);
1466
+ }
1372
1467
  }
1373
1468
  // Add user-defined tools
1374
1469
  for (const [name, tool] of options.tools) {
@@ -1400,6 +1495,16 @@ async function buildSystemPrompt(options) {
1400
1495
  '- If you are stuck, either choose one different tool or call finish(false).',
1401
1496
  '- Once enough information exists, stop planning, set results if needed, and finish.',
1402
1497
  ].join('\n');
1498
+ if (options.conversational) {
1499
+ const chatTemplate = await readSystemPromptAsset('chat', { workspacePath: options.workspacePath });
1500
+ return chatTemplate
1501
+ .replace('{TOOL_DESCRIPTIONS}', toolDescriptions.join('\n'))
1502
+ .replace('{ENVIRONMENT_SECTION}', describeEnvironment({
1503
+ workspacePath: options.workspacePath,
1504
+ environment: options.environment,
1505
+ capabilityTool: options.capabilityTool,
1506
+ }) + describeCurrentTime());
1507
+ }
1403
1508
  const template = await readSystemPromptAsset('task', { workspacePath: options.workspacePath });
1404
1509
  return template
1405
1510
  .replace('{TASK_DESCRIPTION}', options.taskDescription)
@@ -1409,6 +1514,7 @@ async function buildSystemPrompt(options) {
1409
1514
  .replace('{ENVIRONMENT_SECTION}', describeEnvironment({
1410
1515
  workspacePath: options.workspacePath,
1411
1516
  environment: options.environment,
1517
+ capabilityTool: options.capabilityTool,
1412
1518
  }) + describeCurrentTime());
1413
1519
  }
1414
1520
  //# sourceMappingURL=agent.js.map