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.
- package/README.md +86 -3
- package/dist/agent.d.ts +17 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +148 -42
- package/dist/agent.js.map +1 -1
- package/dist/cli/config.d.ts +12 -12
- package/dist/cli/repl.d.ts +13 -1
- package/dist/cli/repl.d.ts.map +1 -1
- package/dist/cli/repl.js +177 -5
- package/dist/cli/repl.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +4 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/forms/index.d.ts +3 -0
- package/dist/forms/index.d.ts.map +1 -0
- package/dist/forms/index.js +3 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/registry.d.ts +119 -0
- package/dist/forms/registry.d.ts.map +1 -0
- package/dist/forms/registry.js +256 -0
- package/dist/forms/registry.js.map +1 -0
- package/dist/forms/spec.d.ts +193 -0
- package/dist/forms/spec.d.ts.map +1 -0
- package/dist/forms/spec.js +295 -0
- package/dist/forms/spec.js.map +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/prolog/loader.d.ts +18 -0
- package/dist/prolog/loader.d.ts.map +1 -1
- package/dist/prolog/loader.js +26 -1
- package/dist/prolog/loader.js.map +1 -1
- package/dist/prolog-src/deepclause_mi.pl +786 -40
- package/dist/repl/agent.d.ts +11 -0
- package/dist/repl/agent.d.ts.map +1 -1
- package/dist/repl/agent.js +23 -9
- package/dist/repl/agent.js.map +1 -1
- package/dist/repl/form-widget.d.ts +56 -0
- package/dist/repl/form-widget.d.ts.map +1 -0
- package/dist/repl/form-widget.js +289 -0
- package/dist/repl/form-widget.js.map +1 -0
- package/dist/repl/harness-tools.d.ts +4 -1
- package/dist/repl/harness-tools.d.ts.map +1 -1
- package/dist/repl/harness-tools.js +15 -58
- package/dist/repl/harness-tools.js.map +1 -1
- package/dist/repl/history.d.ts +24 -1
- package/dist/repl/history.d.ts.map +1 -1
- package/dist/repl/history.js +76 -8
- package/dist/repl/history.js.map +1 -1
- package/dist/repl/index.d.ts +20 -2
- package/dist/repl/index.d.ts.map +1 -1
- package/dist/repl/index.js +9 -0
- package/dist/repl/index.js.map +1 -1
- package/dist/repl/process-backend.d.ts +2 -2
- package/dist/repl/process-backend.d.ts.map +1 -1
- package/dist/repl/process-executor.d.ts +11 -2
- package/dist/repl/process-executor.d.ts.map +1 -1
- package/dist/repl/process-executor.js +67 -18
- package/dist/repl/process-executor.js.map +1 -1
- package/dist/repl/process-worker.d.ts +35 -4
- package/dist/repl/process-worker.d.ts.map +1 -1
- package/dist/repl/process-worker.js +78 -38
- package/dist/repl/process-worker.js.map +1 -1
- package/dist/repl/processes.d.ts +4 -0
- package/dist/repl/processes.d.ts.map +1 -1
- package/dist/repl/processes.js +20 -0
- package/dist/repl/processes.js.map +1 -1
- package/dist/repl/render.d.ts +17 -0
- package/dist/repl/render.d.ts.map +1 -1
- package/dist/repl/render.js +60 -0
- package/dist/repl/render.js.map +1 -1
- package/dist/repl/session.d.ts +143 -7
- package/dist/repl/session.d.ts.map +1 -1
- package/dist/repl/session.js +317 -20
- package/dist/repl/session.js.map +1 -1
- package/dist/repl/tui/form-view.d.ts +17 -0
- package/dist/repl/tui/form-view.d.ts.map +1 -0
- package/dist/repl/tui/form-view.js +25 -0
- package/dist/repl/tui/form-view.js.map +1 -0
- package/dist/repl/tui/renderer.d.ts +4 -0
- package/dist/repl/tui/renderer.d.ts.map +1 -1
- package/dist/repl/tui/renderer.js +12 -0
- package/dist/repl/tui/renderer.js.map +1 -1
- package/dist/repl/worker-host.d.ts +16 -2
- package/dist/repl/worker-host.d.ts.map +1 -1
- package/dist/repl/worker-host.js +38 -6
- package/dist/repl/worker-host.js.map +1 -1
- package/dist/repl/worker-protocol.d.ts +14 -0
- package/dist/repl/worker-protocol.d.ts.map +1 -1
- package/dist/repl/worker-protocol.js +8 -1
- package/dist/repl/worker-protocol.js.map +1 -1
- package/dist/runner.d.ts +69 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +381 -44
- package/dist/runner.js.map +1 -1
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +12 -0
- package/dist/sdk.js.map +1 -1
- package/dist/system/assets/docs/AGENT_PROMPT.md +452 -18
- package/dist/system/assets/docs/CHAT_PROMPT.md +28 -0
- package/dist/system/assets/docs/DML_AUTHORING.md +8 -4
- package/dist/system/assets/docs/DML_FORMS.md +740 -0
- package/dist/system/assets/docs/DML_REFERENCE.md +239 -4
- package/dist/system/assets/docs/HARNESS_EXAMPLES.md +94 -0
- package/dist/system/assets/index.d.ts +2 -2
- package/dist/system/assets/index.d.ts.map +1 -1
- package/dist/system/assets/index.js +4 -0
- package/dist/system/assets/index.js.map +1 -1
- package/dist/system/environment.d.ts +6 -0
- package/dist/system/environment.d.ts.map +1 -1
- package/dist/system/environment.js +5 -4
- package/dist/system/environment.js.map +1 -1
- package/dist/types.d.ts +48 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
- package/src/prolog-src/deepclause_mi.pl +786 -40
package/dist/cli/repl.js
CHANGED
|
@@ -31,12 +31,15 @@ import { StatusBarComponent } from '../repl/tui/status-bar.js';
|
|
|
31
31
|
import { Container, HStack, ScrollView, Spacer, TuiAltScreen, VStack, matchesKey, } from '@earendil-works/pi-tui';
|
|
32
32
|
import { editorTheme, formatInputAnswer } from '../repl/tui/theme.js';
|
|
33
33
|
import { PromptEditor } from '../repl/tui/editor.js';
|
|
34
|
+
import { FormView } from '../repl/tui/form-view.js';
|
|
35
|
+
import { applyFormKey, initialState, rows } from '../repl/form-widget.js';
|
|
34
36
|
import { loadProjectContext } from '../repl/project-context.js';
|
|
35
37
|
import { createPiBackend, THINKING_LEVELS } from '../repl/backend.js';
|
|
36
38
|
import { createReplToolset } from '../repl/toolset.js';
|
|
37
39
|
import { takeStartupPrologMessages } from '../prolog/loader.js';
|
|
38
40
|
import { createPiImageBackend } from '../repl/imagegen.js';
|
|
39
41
|
import { EnvModelResolver, PiModelResolver } from '../repl/model-resolver.js';
|
|
42
|
+
import { pickForm } from '../repl/form-widget.js';
|
|
40
43
|
import { parseModelId, resolveModelSlotConfig } from '../system/config/model-slots.js';
|
|
41
44
|
import { loadReplConfig, resolveReplCompactionConfig, updateConfig } from './config.js';
|
|
42
45
|
import { renderDiagramImage, resolveMedia, } from '../visualize/index.js';
|
|
@@ -232,6 +235,10 @@ export function classifyReplInput(line) {
|
|
|
232
235
|
export async function runGoalWithRenderer(repl, goal, options) {
|
|
233
236
|
let hadError = false;
|
|
234
237
|
let printedAny = false;
|
|
238
|
+
let answered = false;
|
|
239
|
+
// A turn that suspended *succeeded* — it stopped at a safe point and the host
|
|
240
|
+
// recorded where to continue. Reporting `false.` would read as a failure.
|
|
241
|
+
let suspended = false;
|
|
235
242
|
for await (const event of repl.query(goal, { signal: options.signal })) {
|
|
236
243
|
if (event.type === 'solution') {
|
|
237
244
|
options.renderer.solution(event.solution?.bindings ?? []);
|
|
@@ -243,6 +250,14 @@ export async function runGoalWithRenderer(repl, goal, options) {
|
|
|
243
250
|
hadError = true;
|
|
244
251
|
options.renderer.render(event);
|
|
245
252
|
}
|
|
253
|
+
else if (event.type === 'answer') {
|
|
254
|
+
answered = true;
|
|
255
|
+
options.renderer.render(event);
|
|
256
|
+
}
|
|
257
|
+
else if (event.type === 'suspend') {
|
|
258
|
+
suspended = true;
|
|
259
|
+
options.renderer.render(event);
|
|
260
|
+
}
|
|
246
261
|
else if (event.type === 'finished') {
|
|
247
262
|
// Nothing to print for finished.
|
|
248
263
|
}
|
|
@@ -250,11 +265,63 @@ export async function runGoalWithRenderer(repl, goal, options) {
|
|
|
250
265
|
options.renderer.render(event);
|
|
251
266
|
}
|
|
252
267
|
}
|
|
253
|
-
if (!printedAny && !hadError) {
|
|
254
|
-
|
|
268
|
+
if (!printedAny && !hadError && !suspended) {
|
|
269
|
+
// A goal that committed with `answer/1` succeeded; one that produced neither
|
|
270
|
+
// a solution nor an answer simply failed. (`goalTerminator`/`noSolution`
|
|
271
|
+
// keep `--json` stdout pure, where a raw `false.` would corrupt the stream.)
|
|
272
|
+
if (answered)
|
|
273
|
+
options.renderer.goalTerminator();
|
|
274
|
+
else
|
|
275
|
+
options.renderer.noSolution();
|
|
255
276
|
}
|
|
256
277
|
return hadError ? 1 : 0;
|
|
257
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* The REPL's form interaction (v0): a field-by-field wizard over the existing
|
|
281
|
+
* input channel. The pi-tui form widget is the next step (docs/DML_FORMS.md §9).
|
|
282
|
+
*/
|
|
283
|
+
async function presentFormWidget(repl, form) {
|
|
284
|
+
const spec = form.spec;
|
|
285
|
+
if (!spec)
|
|
286
|
+
return;
|
|
287
|
+
const change = await pickForm({ spec, values: form.values, issues: form.issues });
|
|
288
|
+
repl.session.submitForm(form.formId, change
|
|
289
|
+
? { action: change.action, values: change.values }
|
|
290
|
+
: { action: 'cancel', values: form.values ?? {} });
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* The TUI/non-TTY form interaction: a field-by-field wizard over the existing
|
|
294
|
+
* input channel. The plain-TTY path uses the widget instead.
|
|
295
|
+
*/
|
|
296
|
+
async function presentForm(repl, form) {
|
|
297
|
+
const spec = form.spec;
|
|
298
|
+
if (!spec)
|
|
299
|
+
return;
|
|
300
|
+
const values = { ...(form.values ?? {}) };
|
|
301
|
+
const fields = (spec.fields ?? []).filter((field) => field.type !== 'object' && field.widget !== 'hidden');
|
|
302
|
+
for (const field of fields) {
|
|
303
|
+
const label = field.title ?? field.name;
|
|
304
|
+
const current = values[field.name];
|
|
305
|
+
const hint = current === undefined || current === '' ? '' : ` (${String(current)})`;
|
|
306
|
+
const answer = await repl.session.requestHumanInput(`${label}${field.required ? ' *' : ''}${hint}`);
|
|
307
|
+
if (answer === undefined) {
|
|
308
|
+
repl.session.submitForm(form.formId, { action: 'cancel', values });
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (answer.trim() !== '')
|
|
312
|
+
values[field.name] = coerceFieldValue(field.type, answer.trim());
|
|
313
|
+
}
|
|
314
|
+
repl.session.submitForm(form.formId, { action: 'submit', values });
|
|
315
|
+
}
|
|
316
|
+
function coerceFieldValue(type, raw) {
|
|
317
|
+
if (type === 'number' || type === 'integer') {
|
|
318
|
+
const n = Number(raw);
|
|
319
|
+
return Number.isFinite(n) ? n : raw;
|
|
320
|
+
}
|
|
321
|
+
if (type === 'boolean')
|
|
322
|
+
return /^(y|yes|true|1)$/i.test(raw);
|
|
323
|
+
return raw;
|
|
324
|
+
}
|
|
258
325
|
function selectRenderMode(options) {
|
|
259
326
|
if (options.json)
|
|
260
327
|
return 'json';
|
|
@@ -262,6 +329,18 @@ function selectRenderMode(options) {
|
|
|
262
329
|
return 'plain';
|
|
263
330
|
return process.stdout.isTTY ? 'rich' : 'plain';
|
|
264
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* Should a form re-presented after a rejected submit be cancelled instead?
|
|
334
|
+
*
|
|
335
|
+
* A scripted run (`--answer`) supplies the same canned reply to every prompt. The
|
|
336
|
+
* host re-shows a refused submit so a *person* can correct it — but re-feeding the
|
|
337
|
+
* canned answer would refuse, re-show, refuse … forever. So in that mode the
|
|
338
|
+
* second presentation cancels and the run finishes. A model's own `update_form`
|
|
339
|
+
* carries no issues and is unaffected.
|
|
340
|
+
*/
|
|
341
|
+
export function cancelsScriptedRetry(options, form) {
|
|
342
|
+
return options.answer !== undefined && form.op === 'update' && (form.issues?.length ?? 0) > 0;
|
|
343
|
+
}
|
|
265
344
|
/**
|
|
266
345
|
* Build the unified input channel from CLI flags (proposal §13.6):
|
|
267
346
|
* - `--answer` supplies a canned reply; `--no-ask` fails immediately;
|
|
@@ -270,7 +349,20 @@ function selectRenderMode(options) {
|
|
|
270
349
|
*/
|
|
271
350
|
export function buildCliOnInput(options) {
|
|
272
351
|
if (options.answer !== undefined) {
|
|
273
|
-
|
|
352
|
+
// A promptless request is `input/1` — the person's next chat message. A
|
|
353
|
+
// scripted run has exactly one conversation message; replaying it would make
|
|
354
|
+
// a chat loop turn forever, so after it there is nobody to talk to and the
|
|
355
|
+
// conversation ends. Prompted requests (a form field, a question) are not
|
|
356
|
+
// limited: the same canned reply answers each one.
|
|
357
|
+
let usedForMessage = false;
|
|
358
|
+
return async (prompt) => {
|
|
359
|
+
if (prompt === '') {
|
|
360
|
+
if (usedForMessage)
|
|
361
|
+
return undefined;
|
|
362
|
+
usedForMessage = true;
|
|
363
|
+
}
|
|
364
|
+
return options.answer;
|
|
365
|
+
};
|
|
274
366
|
}
|
|
275
367
|
if (options.noAsk) {
|
|
276
368
|
return async () => undefined;
|
|
@@ -379,7 +471,10 @@ export async function createReplRuntime(options) {
|
|
|
379
471
|
llmBackend = pi.backend;
|
|
380
472
|
harnessModel = pi.modelId.split('/').slice(1).join('/') || pi.modelId;
|
|
381
473
|
harnessProvider = pi.provider ?? harnessProvider;
|
|
382
|
-
contextWindow = pi.contextWindow
|
|
474
|
+
contextWindow = pi.contextWindow ?? resolveModelSlotConfig(config, 'run', {
|
|
475
|
+
modelId: options.model ?? config.models.run,
|
|
476
|
+
temperature: options.temperature ?? config.temperatures.run,
|
|
477
|
+
}).contextWindow;
|
|
383
478
|
reasoning = pi.reasoning;
|
|
384
479
|
thinkingLevel = pi.thinkingLevel;
|
|
385
480
|
modelResolver = new PiModelResolver(pi.registry);
|
|
@@ -499,6 +594,15 @@ export async function createReplRuntime(options) {
|
|
|
499
594
|
judgeSelection,
|
|
500
595
|
imageBackend,
|
|
501
596
|
modelResolver,
|
|
597
|
+
onForm: async (form) => {
|
|
598
|
+
if (cancelsScriptedRetry(options, form)) {
|
|
599
|
+
repl.session.submitForm(form.formId, { action: 'cancel', values: form.values ?? {} });
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
return options.plain && process.stdin.isTTY && process.stdout.isTTY
|
|
603
|
+
? presentFormWidget(repl, form)
|
|
604
|
+
: presentForm(repl, form);
|
|
605
|
+
},
|
|
502
606
|
history: historyOptions,
|
|
503
607
|
harness: harnessName,
|
|
504
608
|
execution: options.execution,
|
|
@@ -553,6 +657,7 @@ export async function createReplRuntime(options) {
|
|
|
553
657
|
historyEnabled: Boolean(historyOptions),
|
|
554
658
|
inputHistory,
|
|
555
659
|
statusSnapshot: () => statusTracker.snapshot(),
|
|
660
|
+
observeEvent: (event) => statusTracker.observe(event),
|
|
556
661
|
tuiMode: options.tui ?? config.repl.tui ?? 'auto',
|
|
557
662
|
setOnInput: (handler) => repl.setOnInput(handler),
|
|
558
663
|
missingCredential,
|
|
@@ -637,6 +742,28 @@ async function printBanner(runtime, loaded) {
|
|
|
637
742
|
* starting blank. Bounded, and skipped when the session was not resumed or
|
|
638
743
|
* tracing was off.
|
|
639
744
|
*/
|
|
745
|
+
/**
|
|
746
|
+
* Continue a session that was suspended with `suspend/1`.
|
|
747
|
+
*
|
|
748
|
+
* The turn stopped at a safe point and the host wrote the goal that resumes it
|
|
749
|
+
* (docs/DML_REPL_SUSPEND_RESUME.md). Resuming means running that goal once the
|
|
750
|
+
* harness and memory are restored — so `--session` picks the program up where it
|
|
751
|
+
* left off instead of dropping the user into a half-finished state. The record is
|
|
752
|
+
* cleared first: if the continuation suspends again it writes a fresh one.
|
|
753
|
+
*/
|
|
754
|
+
async function resumeSuspendedContinuation(runtime) {
|
|
755
|
+
const pending = runtime.repl.session.pendingSuspension?.();
|
|
756
|
+
if (!pending)
|
|
757
|
+
return;
|
|
758
|
+
runtime.renderer.writeLine(`resuming the suspended continuation: ${pending.continuation}`);
|
|
759
|
+
await runtime.repl.session.clearSuspension?.();
|
|
760
|
+
try {
|
|
761
|
+
await runGoalWithRenderer(runtime.repl, pending.continuation, { renderer: runtime.renderer });
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
runtime.renderer.writeLine(`could not resume ${pending.continuation}: ${String(error.message)}`);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
640
767
|
async function renderResumedSession(runtime) {
|
|
641
768
|
const events = await runtime.repl.resumedTranscript().catch(() => []);
|
|
642
769
|
if (events.length === 0)
|
|
@@ -1108,7 +1235,10 @@ export async function runInteractiveGoal(repl, renderer, goal, options, ask, sig
|
|
|
1108
1235
|
finally {
|
|
1109
1236
|
await iterator.return?.(undefined);
|
|
1110
1237
|
}
|
|
1111
|
-
|
|
1238
|
+
if (answered)
|
|
1239
|
+
renderer.goalTerminator();
|
|
1240
|
+
else
|
|
1241
|
+
renderer.noSolution();
|
|
1112
1242
|
}
|
|
1113
1243
|
/**
|
|
1114
1244
|
* Interactive TUI for the REPL: a scrolling transcript, a fixed editor, and a
|
|
@@ -1141,6 +1271,9 @@ export async function runTui(runtime, options, onExit, loaded = []) {
|
|
|
1141
1271
|
const working = new WorkingLine(tui);
|
|
1142
1272
|
const renderer = new TuiRenderer({
|
|
1143
1273
|
document,
|
|
1274
|
+
// The TUI draws through its own switch, but the status tracker must still see
|
|
1275
|
+
// every event (usage, model_call, …) or the footer loses its counters.
|
|
1276
|
+
onEvent: (event) => runtime.observeEvent(event),
|
|
1144
1277
|
onWorking: (label) => {
|
|
1145
1278
|
working.set(label);
|
|
1146
1279
|
tui.requestRender();
|
|
@@ -1661,9 +1794,13 @@ export async function runTui(runtime, options, onExit, loaded = []) {
|
|
|
1661
1794
|
// the first and last line of output are not jammed against the chrome. Fixed
|
|
1662
1795
|
// spacers rather than padding inside the scroll view: they do not scroll away and
|
|
1663
1796
|
// do not consume scrollback.
|
|
1797
|
+
// The form panel: shown while a form is open, and the TUI edits its fields
|
|
1798
|
+
// through the editor (one prompt each), updating this panel as it goes.
|
|
1799
|
+
const formView = new FormView();
|
|
1664
1800
|
const dock = new VStack([
|
|
1665
1801
|
{ component: working, basis: 'auto', grow: 0, shrink: 1, minSize: 0 },
|
|
1666
1802
|
{ component: servicesPanel, basis: 'auto', grow: 0, shrink: 1, minSize: 0 },
|
|
1803
|
+
{ component: formView, basis: 'auto', grow: 0, shrink: 1, minSize: 0 },
|
|
1667
1804
|
{ component: new Spacer(1), basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
|
|
1668
1805
|
{ component: editorContainer, basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
|
|
1669
1806
|
{ component: statusBar, basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
|
|
@@ -1690,10 +1827,44 @@ export async function runTui(runtime, options, onExit, loaded = []) {
|
|
|
1690
1827
|
]));
|
|
1691
1828
|
};
|
|
1692
1829
|
applyLayout();
|
|
1830
|
+
// A form opened by the running turn is shown in the dock and edited field by
|
|
1831
|
+
// field through the editor; the plain-TTY path uses the raw widget instead.
|
|
1832
|
+
runtime.repl.session.setFormHandler(async (form) => {
|
|
1833
|
+
const spec = form.spec;
|
|
1834
|
+
if (!spec)
|
|
1835
|
+
return;
|
|
1836
|
+
let state = initialState(spec, form.values ?? {}, form.issues ?? []);
|
|
1837
|
+
formView.setState(state);
|
|
1838
|
+
tui.requestRender();
|
|
1839
|
+
const list = rows(spec);
|
|
1840
|
+
try {
|
|
1841
|
+
for (let index = 0; index < list.length; index += 1) {
|
|
1842
|
+
const row = list[index];
|
|
1843
|
+
if (row.kind !== 'field')
|
|
1844
|
+
continue;
|
|
1845
|
+
const answer = await runtime.repl.session.requestHumanInput(`${row.title}${row.required ? ' *' : ''}`);
|
|
1846
|
+
if (answer === undefined) {
|
|
1847
|
+
runtime.repl.session.submitForm(form.formId, { action: 'cancel', values: state.values });
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
if (answer !== '') {
|
|
1851
|
+
state = applyFormKey({ ...state, focus: index, editing: true, buffer: answer }, 'enter').state;
|
|
1852
|
+
formView.setState(state);
|
|
1853
|
+
tui.requestRender();
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
runtime.repl.session.submitForm(form.formId, { action: 'submit', values: state.values });
|
|
1857
|
+
}
|
|
1858
|
+
finally {
|
|
1859
|
+
formView.setState(undefined);
|
|
1860
|
+
tui.requestRender();
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1693
1863
|
tui.start();
|
|
1694
1864
|
await printBanner(runtime, loaded);
|
|
1695
1865
|
await renderResumedSession(runtime);
|
|
1696
1866
|
tui.requestRender();
|
|
1867
|
+
await resumeSuspendedContinuation(runtime);
|
|
1697
1868
|
try {
|
|
1698
1869
|
while (true) {
|
|
1699
1870
|
// A slash command typed while a turn/command was running: run it now that
|
|
@@ -1883,6 +2054,7 @@ export async function startRepl(options) {
|
|
|
1883
2054
|
}
|
|
1884
2055
|
await printBanner(runtime, options.load ?? []);
|
|
1885
2056
|
await renderResumedSession(runtime);
|
|
2057
|
+
await resumeSuspendedContinuation(runtime);
|
|
1886
2058
|
return await runInteractive(runtime, options, saveHarnessOnExit);
|
|
1887
2059
|
}
|
|
1888
2060
|
finally {
|