@pasko70/pibo 3.0.1 → 3.1.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 +20 -6
- package/compute-image/Dockerfile +47 -0
- package/compute-image/Dockerfile.dockerignore +5 -0
- package/dist/agent-runtime/context-build.js +26 -2
- package/dist/agent-runtime/contract.js +78 -28
- package/dist/agent-runtime/portable-history.js +19 -9
- package/dist/agent-runtime/registry.js +8 -2
- package/dist/agent-runtime/routed-session.js +120 -15
- package/dist/agent-runtimes/codex-native/adapter.js +12 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
- package/dist/agent-runtimes/omp/adapter.js +3 -7
- package/dist/agent-runtimes/pi/adapter.js +5 -3
- package/dist/agent-runtimes/pi/routed-session.js +59 -12
- package/dist/agent-runtimes/pi/runtime.js +6 -0
- package/dist/apps/chat/agent-profiles.js +31 -2
- package/dist/apps/chat/agent-store.js +98 -6
- package/dist/apps/chat/chat-request-normalizers.js +31 -0
- package/dist/apps/chat/data/chat-data-mappers.js +61 -11
- package/dist/apps/chat/data/project-service.js +119 -15
- package/dist/apps/chat/data/room-service.js +21 -0
- package/dist/apps/chat/data/session-query-service.js +33 -10
- package/dist/apps/chat/data/timeline-query-service.js +60 -35
- package/dist/apps/chat/loop-api.js +10 -2
- package/dist/apps/chat/output-compactor.js +144 -21
- package/dist/apps/chat/output-event-policy.js +179 -2
- package/dist/apps/chat/stream.js +16 -7
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +317 -86
- package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CUQJqggN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-ClUlQWYN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-Djul9BmZ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-GD0JGdCi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-W5HOqHym.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BcjkX-iP.js +228 -0
- package/dist/apps/chat-ui/assets/index-CmqRSbBU.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-JvrPUGvI.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/InkSessionApp.js +111 -12
- package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
- package/dist/cli-session/localSessionSource.js +153 -161
- package/dist/cli.js +7 -11
- package/dist/compute/cli.js +35 -16
- package/dist/compute/docker.js +341 -94
- package/dist/config/config.js +3 -0
- package/dist/core/context-build.js +7 -16
- package/dist/core/output-persistence-retry.js +484 -0
- package/dist/core/output-render-sequence.js +331 -0
- package/dist/core/profiles.js +24 -0
- package/dist/core/provider-recovery.js +7 -1
- package/dist/core/session-model.js +37 -1
- package/dist/core/session-router.js +78 -110
- package/dist/data/ingest-service.js +91 -9
- package/dist/data/navigation-store.js +5 -5
- package/dist/data/pibo-store.js +8 -1
- package/dist/data/schema.js +243 -3
- package/dist/data/session-store.js +3 -3
- package/dist/debug/agents.js +91 -89
- package/dist/debug/index.js +46 -2
- package/dist/debug/pty.js +127 -48
- package/dist/gateway/cli.js +4 -0
- package/dist/gateway/web.js +8 -8
- package/dist/local/client.js +3 -2
- package/dist/loops/channel.js +3 -1
- package/dist/loops/cli.js +2 -1
- package/dist/loops/service.js +90 -11
- package/dist/loops/store.js +100 -74
- package/dist/mcp/agent-context.js +13 -26
- package/dist/mcp/commands/info.js +3 -1
- package/dist/mcp/config-command.js +46 -2
- package/dist/mcp/config.js +18 -4
- package/dist/plugins/context-files-store.js +36 -6
- package/dist/plugins/context-files.js +52 -1
- package/dist/plugins/registry.js +2 -0
- package/dist/plugins/user-profile-resources.js +22 -0
- package/dist/previews/base-url.js +34 -0
- package/dist/previews/config.js +2 -32
- package/dist/ralph/store.js +5 -0
- package/dist/reliability/store.js +378 -106
- package/dist/session-ui/terminalRows.js +8 -4
- package/dist/sessions/pibo-data-store.js +145 -0
- package/dist/sessions/store.js +42 -0
- package/dist/setup/cli.js +422 -6
- package/dist/setup/installation-profiles.js +464 -0
- package/dist/shared/deterministic-digest.js +94 -0
- package/dist/shared/trace-engine.js +54 -0
- package/dist/shared/trace-event-projection.js +124 -67
- package/dist/shared/trace-history.js +47 -12
- package/dist/shared/trace-live-reducer.js +23 -4
- package/dist/shared/trace-nodes.js +17 -28
- package/dist/shared/trace-order.js +41 -5
- package/dist/shared/trace-page-merge.js +44 -14
- package/dist/shared/trace-patch-nodes.js +2 -0
- package/dist/shared/trace-subagent-links.js +4 -1
- package/dist/shared/trace-tool-identity.js +15 -8
- package/dist/signals/projector.js +28 -6
- package/dist/subagents/observation-query.js +121 -0
- package/dist/subagents/tool.js +7 -3
- package/dist/tools/agent-browser-leases.js +58 -23
- package/dist/tools/agent-browser-wrapper.js +1 -1
- package/dist/tools/browser-use-leases.js +129 -16
- package/dist/tools/browser-use-wrapper.js +1 -1
- package/dist/tools/index.js +27 -11
- package/dist/tools/python-runtime.js +10 -3
- package/dist/tools/registry.js +1 -1
- package/dist/tools/runtime/node-backend.js +16 -5
- package/dist/tools/runtime/node-worker-source.js +112 -33
- package/dist/tools/runtime/python-backend.js +16 -5
- package/dist/tools/runtime/python-worker-source.js +167 -16
- package/dist/tools/runtime/registry.js +23 -10
- package/dist/user-skills/store.js +0 -1
- package/docs/ops/vscode-extension-release.md +9 -1
- package/npm-shrinkwrap.json +9 -2
- package/package.json +9 -2
- package/scripts/docker-entrypoint.sh +46 -0
- package/scripts/prepare-agent-browser-wrapper.sh +70 -0
- package/scripts/prepare-browser-use-wrapper.sh +255 -0
- package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +0 -43
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.1.vsix +0 -0
package/dist/debug/index.js
CHANGED
|
@@ -604,6 +604,8 @@ async function runDebugEvents(args) {
|
|
|
604
604
|
if (!selector)
|
|
605
605
|
throw new Error("pibo debug events <pibo-session-id> show requires <stream-id-or-event-id>");
|
|
606
606
|
const result = inspectDebugEventShow(piboSessionId, resolveDebugStore("chat"), selector, { ...detailOptions(options), payload: options.payload, raw: options.raw, field: options.field });
|
|
607
|
+
if (!result.event)
|
|
608
|
+
process.exitCode = 1;
|
|
607
609
|
if (options.json)
|
|
608
610
|
console.log(formatJson(result));
|
|
609
611
|
else
|
|
@@ -639,7 +641,7 @@ async function runDebugJobs(args) {
|
|
|
639
641
|
if (command === "list") {
|
|
640
642
|
const jobs = reliability.listJobs({ queue: options.queue, limit: options.limit ? Number(options.limit) : undefined });
|
|
641
643
|
if (options.json)
|
|
642
|
-
console.log(formatJson({ jobs }));
|
|
644
|
+
console.log(formatJson({ jobs: jobs.map(observableJobRow) }));
|
|
643
645
|
else
|
|
644
646
|
console.log(formatRows(jobs.map(compactJobRow)));
|
|
645
647
|
return;
|
|
@@ -647,7 +649,7 @@ async function runDebugJobs(args) {
|
|
|
647
649
|
if (command === "dead") {
|
|
648
650
|
const jobs = reliability.listDead({ queue: options.queue, limit: options.limit ? Number(options.limit) : undefined });
|
|
649
651
|
if (options.json)
|
|
650
|
-
console.log(formatJson({ jobs }));
|
|
652
|
+
console.log(formatJson({ jobs: jobs.map(observableDeadJobRow) }));
|
|
651
653
|
else
|
|
652
654
|
console.log(formatRows(jobs.map(compactDeadJobRow)));
|
|
653
655
|
return;
|
|
@@ -976,26 +978,68 @@ function compactEventRow(event) {
|
|
|
976
978
|
};
|
|
977
979
|
}
|
|
978
980
|
function compactJobRow(job) {
|
|
981
|
+
const correlation = outputPersistenceJobCorrelation(job.payload);
|
|
979
982
|
return {
|
|
980
983
|
jobId: job.jobId,
|
|
981
984
|
queue: job.queue,
|
|
982
985
|
state: job.state,
|
|
983
986
|
runAt: job.runAt,
|
|
984
987
|
attempts: `${job.attempts}/${job.maxAttempts}`,
|
|
988
|
+
piboSessionId: correlation.piboSessionId,
|
|
989
|
+
eventId: correlation.eventId,
|
|
990
|
+
phase: correlation.phase,
|
|
985
991
|
workerId: job.workerId,
|
|
986
992
|
lastError: job.lastError,
|
|
987
993
|
};
|
|
988
994
|
}
|
|
989
995
|
function compactDeadJobRow(job) {
|
|
996
|
+
const correlation = outputPersistenceJobCorrelation(job.payload);
|
|
990
997
|
return {
|
|
991
998
|
jobId: job.jobId,
|
|
992
999
|
queue: job.queue,
|
|
993
1000
|
attempts: `${job.attempts}/${job.maxAttempts}`,
|
|
1001
|
+
piboSessionId: correlation.piboSessionId,
|
|
1002
|
+
eventId: correlation.eventId,
|
|
1003
|
+
phase: correlation.phase,
|
|
994
1004
|
deadAt: job.deadAt,
|
|
995
1005
|
deadReason: job.deadReason,
|
|
996
1006
|
lastError: job.lastError,
|
|
997
1007
|
};
|
|
998
1008
|
}
|
|
1009
|
+
function observableJobRow(job) {
|
|
1010
|
+
return { ...compactJobRow(job), attempts: job.attempts, maxAttempts: job.maxAttempts };
|
|
1011
|
+
}
|
|
1012
|
+
function observableDeadJobRow(job) {
|
|
1013
|
+
return { ...compactDeadJobRow(job), attempts: job.attempts, maxAttempts: job.maxAttempts };
|
|
1014
|
+
}
|
|
1015
|
+
function outputPersistenceJobCorrelation(payload) {
|
|
1016
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
1017
|
+
return {};
|
|
1018
|
+
const envelope = payload;
|
|
1019
|
+
const state = envelope.state;
|
|
1020
|
+
let phase;
|
|
1021
|
+
if (state && typeof state === "object" && !Array.isArray(state)) {
|
|
1022
|
+
const deliveries = state.deliveries;
|
|
1023
|
+
if (Array.isArray(deliveries) && deliveries.length) {
|
|
1024
|
+
const rows = deliveries.filter((delivery) => !!delivery && typeof delivery === "object" && !Array.isArray(delivery));
|
|
1025
|
+
if (rows.length === deliveries.length) {
|
|
1026
|
+
if (rows.every((delivery) => delivery.sideEffectsDelivered === true || delivery.persisted === true))
|
|
1027
|
+
phase = "delivered";
|
|
1028
|
+
else if (rows.every((delivery) => delivery.reliabilityDelivered === true))
|
|
1029
|
+
phase = "side-effects";
|
|
1030
|
+
else if (rows.every((delivery) => delivery.v2 !== undefined))
|
|
1031
|
+
phase = "reliability";
|
|
1032
|
+
else
|
|
1033
|
+
phase = "v2";
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
return {
|
|
1038
|
+
...(typeof envelope.piboSessionId === "string" ? { piboSessionId: envelope.piboSessionId } : {}),
|
|
1039
|
+
...(typeof envelope.eventId === "string" ? { eventId: envelope.eventId } : {}),
|
|
1040
|
+
...(phase ? { phase } : {}),
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
999
1043
|
function compactRunRow(run) {
|
|
1000
1044
|
return {
|
|
1001
1045
|
runId: run.runId,
|
package/dist/debug/pty.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { performance } from "node:perf_hooks";
|
|
4
5
|
const DEFAULT_ROWS = 24;
|
|
5
6
|
const DEFAULT_COLS = 100;
|
|
6
7
|
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
@@ -95,6 +96,12 @@ if exit_code is None:
|
|
|
95
96
|
exit_code = proc.wait(timeout=1)
|
|
96
97
|
sys.exit(exit_code if exit_code is not None else 1)
|
|
97
98
|
`;
|
|
99
|
+
class WallClockTimeoutError extends Error {
|
|
100
|
+
constructor(timeoutMs) {
|
|
101
|
+
super(`PTY command timed out after ${timeoutMs}ms`);
|
|
102
|
+
this.name = "WallClockTimeoutError";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
98
105
|
export async function runDebugPty(args) {
|
|
99
106
|
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
100
107
|
printPtyDiscovery();
|
|
@@ -409,26 +416,31 @@ function parseSteps(value, source) {
|
|
|
409
416
|
}
|
|
410
417
|
async function executePtyScenario(input, options) {
|
|
411
418
|
const scenario = normalizeScenario(input, options);
|
|
412
|
-
const
|
|
413
|
-
const
|
|
419
|
+
const startedAt = new Date().toISOString();
|
|
420
|
+
const started = performance.now();
|
|
421
|
+
const deadline = createDeadline(scenario.timeoutMs, started);
|
|
414
422
|
let runner;
|
|
415
423
|
const assertions = [];
|
|
416
424
|
let iterations = 0;
|
|
417
425
|
let stopReason = "completed";
|
|
426
|
+
let exitCode = null;
|
|
427
|
+
let signal = null;
|
|
418
428
|
try {
|
|
419
429
|
validateProviderSafety(scenario, options);
|
|
420
430
|
runner = await startRunner(scenario);
|
|
421
|
-
|
|
431
|
+
throwIfDeadlineExpired(deadline);
|
|
432
|
+
let lastOutputAt = performance.now();
|
|
422
433
|
let outputLength = 0;
|
|
423
434
|
const refreshOutputTimestamp = () => {
|
|
424
435
|
const raw = runner?.getRawOutput() ?? "";
|
|
425
436
|
if (raw.length !== outputLength) {
|
|
426
437
|
outputLength = raw.length;
|
|
427
|
-
lastOutputAt =
|
|
438
|
+
lastOutputAt = performance.now();
|
|
428
439
|
}
|
|
429
440
|
return lastOutputAt;
|
|
430
441
|
};
|
|
431
442
|
for (const step of scenario.steps) {
|
|
443
|
+
throwIfDeadlineExpired(deadline);
|
|
432
444
|
if (step.iteration === true) {
|
|
433
445
|
iterations += 1;
|
|
434
446
|
if (scenario.providerMode === "real" && scenario.maxIterations !== undefined && iterations > scenario.maxIterations) {
|
|
@@ -437,7 +449,8 @@ async function executePtyScenario(input, options) {
|
|
|
437
449
|
}
|
|
438
450
|
}
|
|
439
451
|
await checkIdle(runner, refreshOutputTimestamp, scenario.idleTimeoutMs);
|
|
440
|
-
await runStep(runner, scenario, step, assertions, refreshOutputTimestamp);
|
|
452
|
+
await runStep(runner, scenario, step, assertions, refreshOutputTimestamp, deadline);
|
|
453
|
+
throwIfDeadlineExpired(deadline);
|
|
441
454
|
refreshOutputTimestamp();
|
|
442
455
|
const clean = cleanTerminalText(runner.getRawOutput());
|
|
443
456
|
const matchedStop = scenario.stopPatterns.find((pattern) => clean.includes(pattern));
|
|
@@ -446,32 +459,40 @@ async function executePtyScenario(input, options) {
|
|
|
446
459
|
break;
|
|
447
460
|
}
|
|
448
461
|
}
|
|
449
|
-
const
|
|
462
|
+
const stoppedByPattern = stopReason.startsWith("stop_pattern:");
|
|
463
|
+
if (stoppedByPattern)
|
|
464
|
+
await runner.terminate(stopReason);
|
|
465
|
+
const exit = await waitForExitOrIdle(runner, deadline, scenario.idleTimeoutMs, refreshOutputTimestamp);
|
|
466
|
+
exitCode = exit.exitCode;
|
|
467
|
+
signal = exit.signal;
|
|
450
468
|
if (exit.idleTimedOut) {
|
|
451
469
|
stopReason = "idle_timeout";
|
|
452
470
|
throw new Error(`PTY command produced no output for ${scenario.idleTimeoutMs}ms`);
|
|
453
471
|
}
|
|
454
472
|
if (exit.timedOut) {
|
|
455
|
-
|
|
456
|
-
throw new Error(`PTY command timed out after ${scenario.timeoutMs}ms`);
|
|
473
|
+
throw new WallClockTimeoutError(scenario.timeoutMs);
|
|
457
474
|
}
|
|
475
|
+
throwIfDeadlineExpired(deadline);
|
|
458
476
|
const clean = cleanTerminalText(runner.getRawOutput());
|
|
459
477
|
assertPatterns(clean, scenario.expect, scenario.reject, assertions);
|
|
460
|
-
|
|
478
|
+
throwIfDeadlineExpired(deadline);
|
|
479
|
+
if (exit.exitCode !== 0 && !stoppedByPattern) {
|
|
461
480
|
stopReason = `exit_code:${exit.exitCode ?? "signal"}`;
|
|
462
481
|
throw new Error(`PTY command exited with status ${exit.exitCode ?? exit.signal ?? "unknown"}`);
|
|
463
482
|
}
|
|
464
|
-
const result = buildResult(scenario, runner, true, assertions, iterations, started, startedAt, stopReason,
|
|
483
|
+
const result = buildResult(scenario, runner, true, assertions, iterations, started, startedAt, stopReason, exitCode, signal);
|
|
465
484
|
if (scenario.writeArtifactsOnSuccess)
|
|
466
485
|
result.artifactDir = await writeArtifacts(scenario, result);
|
|
467
486
|
return result;
|
|
468
487
|
}
|
|
469
488
|
catch (error) {
|
|
470
|
-
if (stopReason === "completed")
|
|
489
|
+
if (error instanceof WallClockTimeoutError && stopReason === "completed")
|
|
490
|
+
stopReason = "wall_clock_timeout";
|
|
491
|
+
else if (stopReason === "completed")
|
|
471
492
|
stopReason = runner ? "error" : "preflight_error";
|
|
472
493
|
if (runner)
|
|
473
494
|
await runner.terminate(stopReason);
|
|
474
|
-
const result = buildResult(scenario, runner, false, assertions, iterations, started, startedAt, stopReason,
|
|
495
|
+
const result = buildResult(scenario, runner, false, assertions, iterations, started, startedAt, stopReason, exitCode, signal);
|
|
475
496
|
result.artifactDir = await writeArtifacts(scenario, result, error instanceof Error ? error.message : String(error));
|
|
476
497
|
const message = `${error instanceof Error ? error.message : String(error)}\nPTY artifacts: ${result.artifactDir}`;
|
|
477
498
|
throw new Error(message);
|
|
@@ -601,6 +622,26 @@ function wrapChild(child, backend, ptyMethod) {
|
|
|
601
622
|
driverStderr += `${error.message}\n`;
|
|
602
623
|
events.push({ t: Date.now(), source: "system", kind: "error", detail: error.message });
|
|
603
624
|
});
|
|
625
|
+
const waitForExit = async (timeoutMs) => {
|
|
626
|
+
if (exitState)
|
|
627
|
+
return { ...exitState, timedOut: false };
|
|
628
|
+
if (timeoutMs <= 0)
|
|
629
|
+
return { exitCode: null, signal: null, timedOut: true };
|
|
630
|
+
return new Promise((resolve) => {
|
|
631
|
+
let settled = false;
|
|
632
|
+
let timer;
|
|
633
|
+
const finish = (value) => {
|
|
634
|
+
if (settled)
|
|
635
|
+
return;
|
|
636
|
+
settled = true;
|
|
637
|
+
if (timer)
|
|
638
|
+
clearTimeout(timer);
|
|
639
|
+
resolve(value);
|
|
640
|
+
};
|
|
641
|
+
exitPromise.then((value) => finish({ ...value, timedOut: false }));
|
|
642
|
+
timer = setTimeout(() => finish({ exitCode: null, signal: null, timedOut: true }), timeoutMs);
|
|
643
|
+
});
|
|
644
|
+
};
|
|
604
645
|
return {
|
|
605
646
|
backend,
|
|
606
647
|
ptyMethod,
|
|
@@ -615,51 +656,46 @@ function wrapChild(child, backend, ptyMethod) {
|
|
|
615
656
|
if (exitState)
|
|
616
657
|
return;
|
|
617
658
|
child.kill("SIGTERM");
|
|
618
|
-
await
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
child.kill("SIGKILL");
|
|
623
|
-
}),
|
|
624
|
-
]);
|
|
625
|
-
},
|
|
626
|
-
async waitForExit(timeoutMs) {
|
|
627
|
-
if (exitState)
|
|
628
|
-
return { ...exitState, timedOut: false };
|
|
629
|
-
const timeout = delay(timeoutMs).then(() => ({ exitCode: null, signal: null, timedOut: true }));
|
|
630
|
-
const exit = exitPromise.then((value) => ({ ...value, timedOut: false }));
|
|
631
|
-
return Promise.race([exit, timeout]);
|
|
659
|
+
if (await waitForChildExit(exitPromise, 1_000))
|
|
660
|
+
return;
|
|
661
|
+
child.kill("SIGKILL");
|
|
662
|
+
await waitForChildExit(exitPromise, 1_000);
|
|
632
663
|
},
|
|
664
|
+
waitForExit,
|
|
633
665
|
getRawOutput: () => rawOutput,
|
|
634
666
|
getDriverStderr: () => driverStderr,
|
|
635
667
|
getEvents: () => events,
|
|
636
668
|
};
|
|
637
669
|
}
|
|
638
|
-
async function runStep(runner, scenario, step, assertions, markOutput) {
|
|
670
|
+
async function runStep(runner, scenario, step, assertions, markOutput, deadline) {
|
|
639
671
|
if (step.waitFor !== undefined) {
|
|
640
|
-
await waitForText(runner, step.waitFor, step.timeoutMs ?? scenario.timeoutMs, scenario.idleTimeoutMs, markOutput);
|
|
672
|
+
await waitForText(runner, step.waitFor, step.timeoutMs ?? scenario.timeoutMs, scenario.idleTimeoutMs, markOutput, deadline);
|
|
641
673
|
return;
|
|
642
674
|
}
|
|
643
675
|
if (step.typeText !== undefined) {
|
|
644
676
|
for (const char of step.typeText) {
|
|
677
|
+
throwIfDeadlineExpired(deadline);
|
|
645
678
|
await runner.write(char);
|
|
646
|
-
|
|
647
|
-
|
|
679
|
+
throwIfDeadlineExpired(deadline);
|
|
680
|
+
await delayWithinDeadline(scenario.inputDelayMs, deadline);
|
|
648
681
|
}
|
|
649
682
|
return;
|
|
650
683
|
}
|
|
651
684
|
if (step.writeBytes !== undefined) {
|
|
685
|
+
throwIfDeadlineExpired(deadline);
|
|
652
686
|
await runner.write(step.writeBytes);
|
|
687
|
+
throwIfDeadlineExpired(deadline);
|
|
653
688
|
return;
|
|
654
689
|
}
|
|
655
690
|
if (step.press !== undefined) {
|
|
691
|
+
throwIfDeadlineExpired(deadline);
|
|
656
692
|
await runner.write(keySequence(step.press));
|
|
657
|
-
|
|
658
|
-
|
|
693
|
+
throwIfDeadlineExpired(deadline);
|
|
694
|
+
await delayWithinDeadline(scenario.inputDelayMs, deadline);
|
|
659
695
|
return;
|
|
660
696
|
}
|
|
661
697
|
if (step.sleepMs !== undefined) {
|
|
662
|
-
await
|
|
698
|
+
await delayWithinDeadline(step.sleepMs, deadline);
|
|
663
699
|
return;
|
|
664
700
|
}
|
|
665
701
|
const clean = cleanTerminalText(runner.getRawOutput());
|
|
@@ -670,35 +706,41 @@ async function runStep(runner, scenario, step, assertions, markOutput) {
|
|
|
670
706
|
if (step.reject !== undefined)
|
|
671
707
|
assertPatterns(clean, [], [step.reject], assertions);
|
|
672
708
|
}
|
|
673
|
-
async function waitForText(runner, pattern, timeoutMs, idleTimeoutMs, markOutput) {
|
|
674
|
-
const
|
|
675
|
-
while (
|
|
709
|
+
async function waitForText(runner, pattern, timeoutMs, idleTimeoutMs, markOutput, globalDeadline) {
|
|
710
|
+
const stepDeadline = performance.now() + timeoutMs;
|
|
711
|
+
while (true) {
|
|
712
|
+
throwIfDeadlineExpired(globalDeadline);
|
|
676
713
|
const lastOutputAt = markOutput();
|
|
677
714
|
if (cleanTerminalText(runner.getRawOutput()).includes(pattern))
|
|
678
715
|
return;
|
|
679
|
-
|
|
716
|
+
const now = performance.now();
|
|
717
|
+
if (now - lastOutputAt > idleTimeoutMs) {
|
|
680
718
|
await runner.terminate("idle_timeout");
|
|
681
719
|
throw new Error(`PTY command produced no output for ${idleTimeoutMs}ms while waiting for ${JSON.stringify(pattern)}`);
|
|
682
720
|
}
|
|
683
|
-
|
|
721
|
+
if (now >= stepDeadline)
|
|
722
|
+
break;
|
|
723
|
+
await delay(Math.max(1, Math.min(50, stepDeadline - now, remainingMs(globalDeadline), idleTimeoutMs - (now - lastOutputAt))));
|
|
684
724
|
}
|
|
685
725
|
throw new Error(`Timed out waiting for PTY output ${JSON.stringify(pattern)} after ${timeoutMs}ms`);
|
|
686
726
|
}
|
|
687
|
-
async function waitForExitOrIdle(runner,
|
|
688
|
-
|
|
689
|
-
while (Date.now() < deadline) {
|
|
727
|
+
async function waitForExitOrIdle(runner, deadline, idleTimeoutMs, markOutput) {
|
|
728
|
+
while (remainingMs(deadline) > 0) {
|
|
690
729
|
const lastOutputAt = markOutput();
|
|
691
|
-
const remaining = Math.max(1, Math.min(100, deadline
|
|
730
|
+
const remaining = Math.max(1, Math.min(100, remainingMs(deadline)));
|
|
692
731
|
const exit = await runner.waitForExit(remaining);
|
|
693
|
-
if (!exit.timedOut)
|
|
732
|
+
if (!exit.timedOut) {
|
|
733
|
+
if (remainingMs(deadline) <= 0)
|
|
734
|
+
return { exitCode: null, signal: null, timedOut: true, idleTimedOut: false };
|
|
694
735
|
return { ...exit, timedOut: false, idleTimedOut: false };
|
|
695
|
-
|
|
736
|
+
}
|
|
737
|
+
if (performance.now() - lastOutputAt > idleTimeoutMs)
|
|
696
738
|
return { exitCode: null, signal: null, timedOut: false, idleTimedOut: true };
|
|
697
739
|
}
|
|
698
740
|
return { exitCode: null, signal: null, timedOut: true, idleTimedOut: false };
|
|
699
741
|
}
|
|
700
742
|
async function checkIdle(runner, lastOutputAt, idleTimeoutMs) {
|
|
701
|
-
if (
|
|
743
|
+
if (performance.now() - lastOutputAt() > idleTimeoutMs) {
|
|
702
744
|
await runner.terminate("idle_timeout");
|
|
703
745
|
throw new Error(`PTY command produced no output for ${idleTimeoutMs}ms`);
|
|
704
746
|
}
|
|
@@ -741,7 +783,7 @@ function cleanTerminalText(raw) {
|
|
|
741
783
|
.replace(/\r/g, "\n");
|
|
742
784
|
}
|
|
743
785
|
function buildResult(scenario, runner, ok, assertions, iterations, started, startedAt, stopReason, exitCode, signal) {
|
|
744
|
-
const
|
|
786
|
+
const endedAt = new Date().toISOString();
|
|
745
787
|
return {
|
|
746
788
|
ok,
|
|
747
789
|
name: scenario.name,
|
|
@@ -750,8 +792,8 @@ function buildResult(scenario, runner, ok, assertions, iterations, started, star
|
|
|
750
792
|
signal,
|
|
751
793
|
stopReason,
|
|
752
794
|
startedAt,
|
|
753
|
-
endedAt
|
|
754
|
-
durationMs:
|
|
795
|
+
endedAt,
|
|
796
|
+
durationMs: Math.round(performance.now() - started),
|
|
755
797
|
rawOutput: runner?.getRawOutput() ?? "",
|
|
756
798
|
driverStderr: runner?.getDriverStderr() ?? "",
|
|
757
799
|
events: runner?.getEvents() ?? [],
|
|
@@ -915,9 +957,46 @@ function optionalStringRecord(value, field, source) {
|
|
|
915
957
|
function safeName(name) {
|
|
916
958
|
return name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "pty-scenario";
|
|
917
959
|
}
|
|
960
|
+
function createDeadline(timeoutMs, started = performance.now()) {
|
|
961
|
+
return { expiresAt: started + timeoutMs, timeoutMs };
|
|
962
|
+
}
|
|
963
|
+
function remainingMs(deadline) {
|
|
964
|
+
return Math.max(0, deadline.expiresAt - performance.now());
|
|
965
|
+
}
|
|
966
|
+
function throwIfDeadlineExpired(deadline) {
|
|
967
|
+
if (remainingMs(deadline) <= 0)
|
|
968
|
+
throw new WallClockTimeoutError(deadline.timeoutMs);
|
|
969
|
+
}
|
|
970
|
+
async function delayWithinDeadline(ms, deadline) {
|
|
971
|
+
throwIfDeadlineExpired(deadline);
|
|
972
|
+
if (ms <= 0)
|
|
973
|
+
return;
|
|
974
|
+
const remaining = remainingMs(deadline);
|
|
975
|
+
if (ms >= remaining) {
|
|
976
|
+
await delay(remaining);
|
|
977
|
+
throw new WallClockTimeoutError(deadline.timeoutMs);
|
|
978
|
+
}
|
|
979
|
+
await delay(ms);
|
|
980
|
+
throwIfDeadlineExpired(deadline);
|
|
981
|
+
}
|
|
918
982
|
function delay(ms) {
|
|
919
983
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
920
984
|
}
|
|
985
|
+
async function waitForChildExit(exitPromise, timeoutMs) {
|
|
986
|
+
let timeout;
|
|
987
|
+
try {
|
|
988
|
+
return await Promise.race([
|
|
989
|
+
exitPromise.then(() => true),
|
|
990
|
+
new Promise((resolve) => {
|
|
991
|
+
timeout = setTimeout(() => resolve(false), timeoutMs);
|
|
992
|
+
}),
|
|
993
|
+
]);
|
|
994
|
+
}
|
|
995
|
+
finally {
|
|
996
|
+
if (timeout)
|
|
997
|
+
clearTimeout(timeout);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
921
1000
|
export const __debugPtyForTests = {
|
|
922
1001
|
cleanTerminalText,
|
|
923
1002
|
keySequence,
|
package/dist/gateway/cli.js
CHANGED
|
@@ -407,6 +407,10 @@ export async function runGatewayCli(argv = process.argv) {
|
|
|
407
407
|
const subcommand = args[1];
|
|
408
408
|
const hasForceFlag = args.includes("--force");
|
|
409
409
|
if (subcommand === "web" || subcommand === "dev") {
|
|
410
|
+
if (args.slice(2).some((arg) => arg === "--help" || arg === "-h")) {
|
|
411
|
+
printGatewayHelp();
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
410
414
|
const handled = await runManagedGatewayCommand(subcommand, args[2], args.slice(3), argv);
|
|
411
415
|
if (!handled) {
|
|
412
416
|
console.error(`Unknown gateway ${subcommand} subcommand: ${args[2] ?? ""}`);
|
package/dist/gateway/web.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { CHAT_WEB_APP_NAME } from "../apps/chat/web-app.js";
|
|
2
2
|
import { createDefaultPiboPlugins } from "../plugins/builtin.js";
|
|
3
3
|
import { createPiboBetterAuthPlugin } from "../plugins/better-auth.js";
|
|
4
|
-
import { createPiboChatCustomAgentProfilesPlugin } from "../plugins/chat-custom-agents.js";
|
|
5
|
-
import { createPiboChatUserSkillsPlugin } from "../plugins/chat-user-skills.js";
|
|
6
4
|
import { createPiboChatWebPlugin } from "../plugins/chat-web.js";
|
|
7
5
|
import { createPiboChatVscodeWebPlugin } from "../plugins/chat-vscode-web.js";
|
|
8
|
-
import {
|
|
6
|
+
import { createPiboUserProfileResourcePlugins } from "../plugins/user-profile-resources.js";
|
|
9
7
|
import { createPiboPreviewPlugin } from "../previews/plugin.js";
|
|
10
8
|
import { createPiboCronPlugin } from "../cron/plugin.js";
|
|
11
9
|
import { createPiboLoopPlugin } from "../loops/plugin.js";
|
|
@@ -154,13 +152,15 @@ export function createWebPiboPluginRegistry(options = {}) {
|
|
|
154
152
|
landingAppName: CHAT_WEB_APP_NAME,
|
|
155
153
|
}),
|
|
156
154
|
createPiboCronPlugin({ cronStorePath: resolvedOptions.chat?.cronStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
...createPiboUserProfileResourcePlugins({
|
|
156
|
+
userSkills: {
|
|
157
|
+
globalRoot: resolvedOptions.chat?.userSkillGlobalRoot,
|
|
158
|
+
workspaceRoot: resolvedOptions.chat?.userSkillWorkspaceRoot,
|
|
159
|
+
},
|
|
160
|
+
contextFiles: resolvedOptions.contextFiles,
|
|
161
|
+
customAgents: { agentStorePath: resolvedOptions.chat?.agentStorePath },
|
|
160
162
|
}),
|
|
161
|
-
createPiboChatCustomAgentProfilesPlugin({ agentStorePath: resolvedOptions.chat?.agentStorePath }),
|
|
162
163
|
createPiboLoopPlugin({ loopStorePath: resolvedOptions.chat?.ralphStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
|
|
163
|
-
createPiboContextFilesPlugin(resolvedOptions.contextFiles),
|
|
164
164
|
createPiboPreviewPlugin(),
|
|
165
165
|
createPiboChatWebPlugin(resolvedOptions.chat),
|
|
166
166
|
createPiboChatVscodeWebPlugin(),
|
package/dist/local/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { PiboSessionRouter } from "../core/session-router.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createPiboProfileFromRegistryOrDefault, resolvePiboProfileNameFromRegistryOrDefault } from "../plugins/builtin.js";
|
|
4
|
+
import { createDefaultPiboUserProfileRegistry } from "../plugins/user-profile-resources.js";
|
|
4
5
|
import { getDefaultPiboWorkspace } from "../core/workspace.js";
|
|
5
6
|
import { InMemoryPiboSessionStore, } from "../sessions/store.js";
|
|
6
7
|
export const LOCAL_TUI_CHANNEL_NAME = "local-tui";
|
|
@@ -52,7 +53,7 @@ export class LocalRoutedTuiClient {
|
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
export function createLocalRoutedTuiClient(options = {}) {
|
|
55
|
-
const registry = options.pluginRegistry ??
|
|
56
|
+
const registry = options.pluginRegistry ?? createDefaultPiboUserProfileRegistry();
|
|
56
57
|
const profileName = resolvePiboProfileNameFromRegistryOrDefault(registry, options.profile);
|
|
57
58
|
const profile = createPiboProfileFromRegistryOrDefault(registry, profileName);
|
|
58
59
|
const sessionName = options.sessionName ?? "default";
|
package/dist/loops/channel.js
CHANGED
|
@@ -4,5 +4,7 @@ let currentLoopService;
|
|
|
4
4
|
export function getPiboLoopService() { return currentLoopService; }
|
|
5
5
|
export function createPiboLoopChannel(options = {}) {
|
|
6
6
|
return { name: 'pibo.loop', kind: 'custom', description: 'Runs continuous Loop Pibo agent jobs.', auth: { mode: 'trusted-local' }, start(context) { if (currentLoopService)
|
|
7
|
-
return; currentLoopService = new PiboLoopService({ ...options, context, store: createDefaultPiboLoopStore({ path: options.loopStorePath }) }); currentLoopService.start(); }, stop() {
|
|
7
|
+
return; currentLoopService = new PiboLoopService({ ...options, context, store: createDefaultPiboLoopStore({ path: options.loopStorePath }) }); currentLoopService.start(); }, async stop() { const service = currentLoopService; if (!service)
|
|
8
|
+
return; await service.stop(); if (currentLoopService === service)
|
|
9
|
+
currentLoopService = undefined; } };
|
|
8
10
|
}
|
package/dist/loops/cli.js
CHANGED
|
@@ -27,7 +27,8 @@ Commands:
|
|
|
27
27
|
|
|
28
28
|
Next: ${commandName} add --help`);
|
|
29
29
|
}
|
|
30
|
-
function targetFromOptions(options) { if (options.room)
|
|
30
|
+
function targetFromOptions(options) { if (options.room && options.defaultChat)
|
|
31
|
+
throw new Error('Choose either --room <room-id> or --default-chat, not both'); if (options.room)
|
|
31
32
|
return { kind: 'room', roomId: options.room }; if (options.defaultChat)
|
|
32
33
|
return { kind: 'default-chat' }; throw new Error('Choose a target: --room <room-id> or --default-chat'); }
|
|
33
34
|
function maybeTargetFromOptions(options) { if (options.room || options.defaultChat)
|
package/dist/loops/service.js
CHANGED
|
@@ -84,6 +84,11 @@ export class PiboLoopService {
|
|
|
84
84
|
random;
|
|
85
85
|
timer;
|
|
86
86
|
activeRuns = 0;
|
|
87
|
+
activeRunWork = new Set();
|
|
88
|
+
pendingRunStarts = 0;
|
|
89
|
+
schedulerWork;
|
|
90
|
+
shutdownPromise;
|
|
91
|
+
ownedWorkWaiters = new Set();
|
|
87
92
|
stopped = true;
|
|
88
93
|
cancelledRuns = new Set();
|
|
89
94
|
browserLeaseHeartbeatTimers = new Map();
|
|
@@ -106,13 +111,52 @@ export class PiboLoopService {
|
|
|
106
111
|
}
|
|
107
112
|
start() { if (!this.stopped)
|
|
108
113
|
return; this.stopped = false; this.store.recoverInterruptedRuns(); this.repairGoalSessionMetadata(); this.unsubscribeProductEvents = this.options.context.subscribeProductEvents?.((event) => this.handleProductEvent(event)); this.unsubscribeOutputEvents = this.options.context.subscribe((event) => this.handleOutputEvent(event)); this.arm(250); }
|
|
109
|
-
stop() {
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
stop() {
|
|
115
|
+
if (this.shutdownPromise)
|
|
116
|
+
return this.shutdownPromise;
|
|
117
|
+
this.stopped = true;
|
|
118
|
+
if (this.timer)
|
|
119
|
+
clearTimeout(this.timer);
|
|
120
|
+
this.timer = undefined;
|
|
121
|
+
for (const timer of this.browserLeaseHeartbeatTimers.values())
|
|
122
|
+
clearInterval(timer);
|
|
123
|
+
this.browserLeaseHeartbeatTimers.clear();
|
|
124
|
+
const closeStores = () => {
|
|
125
|
+
this.unsubscribeProductEvents?.();
|
|
126
|
+
this.unsubscribeProductEvents = undefined;
|
|
127
|
+
this.unsubscribeOutputEvents?.();
|
|
128
|
+
this.unsubscribeOutputEvents = undefined;
|
|
129
|
+
this.dataStore.close();
|
|
130
|
+
this.store.close();
|
|
131
|
+
};
|
|
132
|
+
if (!this.hasOwnedWork()) {
|
|
133
|
+
closeStores();
|
|
134
|
+
this.shutdownPromise = Promise.resolve();
|
|
135
|
+
return this.shutdownPromise;
|
|
136
|
+
}
|
|
137
|
+
this.shutdownPromise = this.drainOwnedWork().then(closeStores);
|
|
138
|
+
return this.shutdownPromise;
|
|
139
|
+
}
|
|
112
140
|
status() { return { enabled: !this.stopped, ...this.store.status() }; }
|
|
113
|
-
async startJob(id) {
|
|
114
|
-
|
|
115
|
-
|
|
141
|
+
async startJob(id) {
|
|
142
|
+
if (this.stopped)
|
|
143
|
+
return undefined;
|
|
144
|
+
this.pendingRunStarts += 1;
|
|
145
|
+
try {
|
|
146
|
+
const job = this.store.updateJob(id, { enabled: true });
|
|
147
|
+
if (!job)
|
|
148
|
+
return undefined;
|
|
149
|
+
const reserved = await this.reserveAfterBeforeRunEvaluation(job);
|
|
150
|
+
if (!reserved)
|
|
151
|
+
return undefined;
|
|
152
|
+
this.launchReservedRun(reserved.job, reserved.run);
|
|
153
|
+
return reserved.run;
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
this.pendingRunStarts -= 1;
|
|
157
|
+
this.notifyOwnedWorkChange();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
116
160
|
stopJob(id) { const job = this.store.requestStop(id); this.armSoon(); return job; }
|
|
117
161
|
removeJob(id) {
|
|
118
162
|
const job = this.store.getJob(id);
|
|
@@ -244,9 +288,17 @@ export class PiboLoopService {
|
|
|
244
288
|
const { loopJobId: _loopJobId, loopRunId: _loopRunId, ...metadata } = session.metadata ?? {};
|
|
245
289
|
this.options.context.updateSession(piboSessionId, { metadata });
|
|
246
290
|
}
|
|
247
|
-
arm(delayMs) {
|
|
248
|
-
|
|
249
|
-
|
|
291
|
+
arm(delayMs) {
|
|
292
|
+
if (this.stopped)
|
|
293
|
+
return;
|
|
294
|
+
if (this.timer)
|
|
295
|
+
clearTimeout(this.timer);
|
|
296
|
+
this.timer = setTimeout(() => {
|
|
297
|
+
const work = this.tick();
|
|
298
|
+
this.schedulerWork = work;
|
|
299
|
+
void work.then(() => this.finishSchedulerWork(work), (error) => { console.error('[loop] scheduler work failed', error); this.finishSchedulerWork(work); });
|
|
300
|
+
}, delayMs ?? this.intervalMs);
|
|
301
|
+
}
|
|
250
302
|
armSoon() { this.arm(250); }
|
|
251
303
|
async tick() { if (this.stopped)
|
|
252
304
|
return; try {
|
|
@@ -257,7 +309,7 @@ export class PiboLoopService {
|
|
|
257
309
|
for (const job of jobs) {
|
|
258
310
|
const reserved = await this.reserveAfterBeforeRunEvaluation(job);
|
|
259
311
|
if (reserved)
|
|
260
|
-
|
|
312
|
+
this.launchReservedRun(reserved.job, reserved.run);
|
|
261
313
|
}
|
|
262
314
|
}
|
|
263
315
|
}
|
|
@@ -280,11 +332,38 @@ export class PiboLoopService {
|
|
|
280
332
|
this.store.applyStopEvaluation({ jobId: fresh.id, evaluation, conditionStates, disable: false });
|
|
281
333
|
if (fresh.mode === 'goal' && !await this.renewGoalBrowserLeases(fresh))
|
|
282
334
|
return undefined;
|
|
283
|
-
|
|
335
|
+
if (this.stopped)
|
|
336
|
+
return undefined;
|
|
337
|
+
const reserved = this.store.reserveAdmittedRun(fresh.id, now);
|
|
284
338
|
if (reserved)
|
|
285
339
|
this.startGoalBrowserLeaseHeartbeat(reserved.job, reserved.run);
|
|
286
340
|
return reserved;
|
|
287
341
|
}
|
|
342
|
+
launchReservedRun(job, run) {
|
|
343
|
+
const work = this.executeReserved(job, run);
|
|
344
|
+
this.activeRunWork.add(work);
|
|
345
|
+
void work.then(() => this.finishActiveRunWork(work), (error) => { console.error(`[loop] active run ${run.id} finalization failed`, error); this.finishActiveRunWork(work); });
|
|
346
|
+
}
|
|
347
|
+
finishActiveRunWork(work) {
|
|
348
|
+
this.activeRunWork.delete(work);
|
|
349
|
+
this.armSoon();
|
|
350
|
+
this.notifyOwnedWorkChange();
|
|
351
|
+
}
|
|
352
|
+
finishSchedulerWork(work) {
|
|
353
|
+
if (this.schedulerWork === work)
|
|
354
|
+
this.schedulerWork = undefined;
|
|
355
|
+
this.notifyOwnedWorkChange();
|
|
356
|
+
}
|
|
357
|
+
hasOwnedWork() { return this.pendingRunStarts > 0 || this.schedulerWork !== undefined || this.activeRunWork.size > 0; }
|
|
358
|
+
async drainOwnedWork() {
|
|
359
|
+
while (this.hasOwnedWork())
|
|
360
|
+
await new Promise((resolve) => this.ownedWorkWaiters.add(resolve));
|
|
361
|
+
}
|
|
362
|
+
notifyOwnedWorkChange() {
|
|
363
|
+
for (const resolve of this.ownedWorkWaiters)
|
|
364
|
+
resolve();
|
|
365
|
+
this.ownedWorkWaiters.clear();
|
|
366
|
+
}
|
|
288
367
|
startGoalBrowserLeaseHeartbeat(job, run) {
|
|
289
368
|
if (job.mode !== 'goal' || (job.resources?.browserLeaseIds?.length ?? 0) === 0)
|
|
290
369
|
return;
|