@pellux/goodvibes-agent 1.5.7 → 1.5.9
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/CHANGELOG.md +16 -3
- package/README.md +1 -1
- package/dist/package/main.js +309 -150
- package/package.json +1 -1
- package/src/agent/memory-prompt.ts +3 -3
- package/src/agent/operator-actions.ts +1 -1
- package/src/agent/prompt-context-receipts.ts +3 -3
- package/src/agent/session-registration.ts +1 -1
- package/src/agent/vibe-file.ts +5 -5
- package/src/audio/player.ts +91 -5
- package/src/audio/spoken-turn-controller.ts +281 -29
- package/src/audio/spoken-turn-wiring.ts +13 -2
- package/src/cli/local-library-command-shared.ts +1 -1
- package/src/cli/memory-command.ts +9 -6
- package/src/cli/resume-relaunch-notice.ts +1 -1
- package/src/cli/tui-startup.ts +1 -1
- package/src/config/credential-status.ts +1 -1
- package/src/config/index.ts +1 -1
- package/src/config/secrets.ts +1 -1
- package/src/core/conversation-rendering.ts +2 -2
- package/src/core/system-message-noise.ts +3 -3
- package/src/core/thinking-overlay.ts +1 -1
- package/src/input/agent-workspace-basic-command-editor-submission.ts +1 -1
- package/src/input/agent-workspace-basic-command-editors.ts +1 -1
- package/src/input/agent-workspace-calendar-connect-editor.ts +1 -1
- package/src/input/agent-workspace-calendar-oauth-editor.ts +1 -1
- package/src/input/agent-workspace-calendar-subscribe-editor.ts +1 -1
- package/src/input/agent-workspace-command-editor-engine.ts +2 -2
- package/src/input/agent-workspace-direct-editor-submission.ts +1 -1
- package/src/input/agent-workspace-email-connect-editor.ts +1 -1
- package/src/input/agent-workspace-live-counters.ts +1 -1
- package/src/input/agent-workspace-settings.ts +1 -1
- package/src/input/agent-workspace-snapshot-builders.ts +2 -2
- package/src/input/agent-workspace-snapshot-config.ts +1 -1
- package/src/input/agent-workspace-snapshot.ts +9 -9
- package/src/input/agent-workspace-types.ts +4 -4
- package/src/input/agent-workspace.ts +1 -1
- package/src/input/commands/calendar-connect-runtime.ts +1 -1
- package/src/input/commands/calendar-subscription-runtime.ts +2 -2
- package/src/input/commands/operator-actions-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +1 -1
- package/src/input/commands/session-workflow.ts +1 -1
- package/src/input/feed-context-factory.ts +4 -4
- package/src/input/handler-feed.ts +9 -9
- package/src/input/handler.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +3 -3
- package/src/input/settings-modal.ts +1 -1
- package/src/main.ts +28 -33
- package/src/renderer/agent-workspace-context-lines.ts +1 -1
- package/src/renderer/startup-theme-probe.ts +1 -1
- package/src/renderer/status-glyphs.ts +2 -2
- package/src/renderer/terminal-bg-probe.ts +1 -1
- package/src/renderer/terminal-escapes.ts +3 -2
- package/src/renderer/theme-mode-config.ts +2 -2
- package/src/renderer/theme.ts +4 -4
- package/src/renderer/thinking.ts +1 -1
- package/src/renderer/ui-factory.ts +1 -1
- package/src/renderer/ui-primitives.ts +2 -2
- package/src/runtime/bootstrap-core.ts +2 -2
- package/src/runtime/bootstrap.ts +3 -3
- package/src/runtime/calendar-boot-refresh.ts +1 -1
- package/src/runtime/lan-scan-consent.ts +1 -1
- package/src/runtime/services.ts +7 -7
- package/src/runtime/terminal-output-guard.ts +1 -1
- package/src/runtime/ui-services.ts +1 -1
- package/src/runtime/unhandled-rejection-guard.ts +41 -0
- package/src/shell/agent-workspace-fullscreen.ts +1 -1
- package/src/shell/terminal-focus-mode.ts +9 -9
- package/src/shell/ui-openers.ts +1 -1
- package/src/tools/agent-harness-operator-methods.ts +1 -1
- package/src/tools/agent-harness-personal-ops-discovery.ts +2 -2
- package/src/tools/agent-harness-personal-ops-lanes.ts +1 -1
- package/src/tools/agent-harness-personal-ops-types.ts +1 -1
- package/src/tools/agent-harness-prompt-context.ts +3 -3
- package/src/tools/agent-local-registry-memory.ts +1 -1
- package/src/tools/agent-operator-method-tool.ts +1 -1
- package/src/version.ts +1 -1
package/dist/package/main.js
CHANGED
|
@@ -459642,23 +459642,6 @@ function createSchema2(db) {
|
|
|
459642
459642
|
`);
|
|
459643
459643
|
db.run(`CREATE INDEX IF NOT EXISTS idx_knowledge_schedules_job_id ON knowledge_schedules(job_id)`);
|
|
459644
459644
|
}
|
|
459645
|
-
function getKnowledgeSchemaStatements() {
|
|
459646
|
-
const statements = [];
|
|
459647
|
-
createSchema2({
|
|
459648
|
-
run(sql) {
|
|
459649
|
-
const normalized = sql.trim();
|
|
459650
|
-
if (normalized.length > 0)
|
|
459651
|
-
statements.push(normalized);
|
|
459652
|
-
}
|
|
459653
|
-
});
|
|
459654
|
-
return statements;
|
|
459655
|
-
}
|
|
459656
|
-
function renderKnowledgeSchemaSql() {
|
|
459657
|
-
return `${getKnowledgeSchemaStatements().map((statement) => statement.endsWith(";") ? statement : `${statement};`).join(`
|
|
459658
|
-
|
|
459659
|
-
`)}
|
|
459660
|
-
`;
|
|
459661
|
-
}
|
|
459662
459645
|
function rowObject(columns, values2) {
|
|
459663
459646
|
return Object.fromEntries(columns.map((column, index) => [column, values2[index]]));
|
|
459664
459647
|
}
|
|
@@ -772974,9 +772957,6 @@ var init_ingest = __esm(() => {
|
|
|
772974
772957
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/browser-history/index.js
|
|
772975
772958
|
var init_browser_history = __esm(() => {
|
|
772976
772959
|
init_ingest();
|
|
772977
|
-
init_discover();
|
|
772978
|
-
init_readers();
|
|
772979
|
-
init_paths2();
|
|
772980
772960
|
});
|
|
772981
772961
|
|
|
772982
772962
|
// node_modules/graphql/jsutils/devAssert.mjs
|
|
@@ -790559,7 +790539,6 @@ var init_semantic = __esm(() => {
|
|
|
790559
790539
|
init_llm();
|
|
790560
790540
|
init_gap_repair();
|
|
790561
790541
|
init_service4();
|
|
790562
|
-
init_self_improvement();
|
|
790563
790542
|
});
|
|
790564
790543
|
|
|
790565
790544
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/home-graph/helpers.js
|
|
@@ -794528,40 +794507,8 @@ var init_map_view = __esm(() => {
|
|
|
794528
794507
|
});
|
|
794529
794508
|
|
|
794530
794509
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/home-graph/types.js
|
|
794531
|
-
var
|
|
794510
|
+
var HOME_GRAPH_CAPABILITIES;
|
|
794532
794511
|
var init_types15 = __esm(() => {
|
|
794533
|
-
HOME_GRAPH_NODE_KINDS = [
|
|
794534
|
-
"ha_home",
|
|
794535
|
-
"ha_entity",
|
|
794536
|
-
"ha_device",
|
|
794537
|
-
"ha_area",
|
|
794538
|
-
"ha_automation",
|
|
794539
|
-
"ha_script",
|
|
794540
|
-
"ha_scene",
|
|
794541
|
-
"ha_label",
|
|
794542
|
-
"ha_integration",
|
|
794543
|
-
"ha_room",
|
|
794544
|
-
"ha_device_passport",
|
|
794545
|
-
"ha_maintenance_item",
|
|
794546
|
-
"ha_troubleshooting_case",
|
|
794547
|
-
"ha_purchase",
|
|
794548
|
-
"ha_network_node"
|
|
794549
|
-
];
|
|
794550
|
-
HOME_GRAPH_RELATIONS = [
|
|
794551
|
-
"controls",
|
|
794552
|
-
"located_in",
|
|
794553
|
-
"belongs_to_device",
|
|
794554
|
-
"has_manual",
|
|
794555
|
-
"has_receipt",
|
|
794556
|
-
"has_warranty",
|
|
794557
|
-
"has_issue",
|
|
794558
|
-
"fixed_by",
|
|
794559
|
-
"uses_battery",
|
|
794560
|
-
"connected_via",
|
|
794561
|
-
"part_of_network",
|
|
794562
|
-
"mentioned_by",
|
|
794563
|
-
"source_for"
|
|
794564
|
-
];
|
|
794565
794512
|
HOME_GRAPH_CAPABILITIES = [
|
|
794566
794513
|
"knowledge-space-isolation",
|
|
794567
794514
|
"snapshot-sync",
|
|
@@ -795968,7 +795915,6 @@ var init_service5 = __esm(() => {
|
|
|
795968
795915
|
var init_home_graph = __esm(() => {
|
|
795969
795916
|
init_service5();
|
|
795970
795917
|
init_extension();
|
|
795971
|
-
init_types15();
|
|
795972
795918
|
});
|
|
795973
795919
|
|
|
795974
795920
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/store-refinement.js
|
|
@@ -798359,14 +798305,10 @@ var init_service6 = __esm(() => {
|
|
|
798359
798305
|
var init_project_planning = __esm(() => {
|
|
798360
798306
|
init_service6();
|
|
798361
798307
|
init_helpers3();
|
|
798362
|
-
init_readiness();
|
|
798363
798308
|
});
|
|
798364
798309
|
|
|
798365
798310
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/persistence.js
|
|
798366
|
-
var init_persistence =
|
|
798367
|
-
init_store_schema();
|
|
798368
|
-
init_store_load();
|
|
798369
|
-
});
|
|
798311
|
+
var init_persistence = () => {};
|
|
798370
798312
|
|
|
798371
798313
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/knowledge/projections.js
|
|
798372
798314
|
class KnowledgeProjectionService {
|
|
@@ -801871,21 +801813,21 @@ var init_service7 = __esm(() => {
|
|
|
801871
801813
|
var exports_knowledge = {};
|
|
801872
801814
|
__export(exports_knowledge, {
|
|
801873
801815
|
withKnowledgeSpace: () => withKnowledgeSpace,
|
|
801874
|
-
uniqKnowledgeValues: () =>
|
|
801875
|
-
stabilizeKnowledgeText: () =>
|
|
801876
|
-
runKnowledgeSemanticSelfImprovement: () =>
|
|
801816
|
+
uniqKnowledgeValues: () => uniq3,
|
|
801817
|
+
stabilizeKnowledgeText: () => stableText2,
|
|
801818
|
+
runKnowledgeSemanticSelfImprovement: () => runKnowledgeSemanticSelfImprovement2,
|
|
801877
801819
|
resolveProjectPlanningSpace: () => resolveProjectPlanningSpace,
|
|
801878
|
-
resolveKnowledgeDbPathFromControlPlaneDir: () =>
|
|
801820
|
+
resolveKnowledgeDbPathFromControlPlaneDir: () => resolveKnowledgeDbPathFromControlPlaneDir2,
|
|
801879
801821
|
renderPacket: () => renderPacket,
|
|
801880
801822
|
renderKnowledgeSchemaSql: () => renderKnowledgeSchemaSql,
|
|
801881
801823
|
renderKnowledgeMap: () => renderKnowledgeMap,
|
|
801882
801824
|
readKnowledgeSearchText: () => readKnowledgeSearchText,
|
|
801883
|
-
readBrowserKnowledgeProfile: () =>
|
|
801825
|
+
readBrowserKnowledgeProfile: () => readBrowserKnowledgeProfile2,
|
|
801884
801826
|
projectPlanningSourceId: () => projectPlanningSourceId,
|
|
801885
801827
|
projectPlanningProjectIdFromPath: () => projectPlanningProjectIdFromPath,
|
|
801886
801828
|
projectPlanningCanonicalUri: () => projectPlanningCanonicalUri,
|
|
801887
801829
|
projectKnowledgeSpaceId: () => projectKnowledgeSpaceId,
|
|
801888
|
-
parseKnowledgeJsonValue: () =>
|
|
801830
|
+
parseKnowledgeJsonValue: () => parseJsonValue2,
|
|
801889
801831
|
normalizeProjectId: () => normalizeProjectId,
|
|
801890
801832
|
normalizeKnowledgeSpaceId: () => normalizeKnowledgeSpaceId,
|
|
801891
801833
|
normalizeHomeAssistantInstallationId: () => normalizeHomeAssistantInstallationId,
|
|
@@ -801893,12 +801835,12 @@ __export(exports_knowledge, {
|
|
|
801893
801835
|
materializeGeneratedKnowledgeProjection: () => materializeGeneratedKnowledgeProjection,
|
|
801894
801836
|
looksLikeRawPdfPayload: () => looksLikeRawPdfPayload,
|
|
801895
801837
|
looksBinaryLikeText: () => looksBinaryLikeText,
|
|
801896
|
-
loadKnowledgeStoreSnapshot: () =>
|
|
801838
|
+
loadKnowledgeStoreSnapshot: () => loadKnowledgeStoreSnapshot2,
|
|
801897
801839
|
listGeneratedKnowledgePages: () => listGeneratedKnowledgePages,
|
|
801898
|
-
listBrowserKinds: () =>
|
|
801840
|
+
listBrowserKinds: () => listBrowserKinds2,
|
|
801899
801841
|
knowledgeSpaceMetadata: () => knowledgeSpaceMetadata,
|
|
801900
801842
|
knowledgePageSourceWeight: () => knowledgePageSourceWeight,
|
|
801901
|
-
knowledgeNowMs: () =>
|
|
801843
|
+
knowledgeNowMs: () => nowMs2,
|
|
801902
801844
|
knowledgeExtractionNeedsRefresh: () => knowledgeExtractionNeedsRefresh,
|
|
801903
801845
|
isUsefulKnowledgePageSourceCandidate: () => isUsefulKnowledgePageSourceCandidate,
|
|
801904
801846
|
isUsefulKnowledgePageSource: () => isUsefulKnowledgePageSource,
|
|
@@ -801918,12 +801860,12 @@ __export(exports_knowledge, {
|
|
|
801918
801860
|
generatedKnowledgeSourceId: () => generatedKnowledgeSourceId,
|
|
801919
801861
|
generatedKnowledgeCanonicalUri: () => generatedKnowledgeCanonicalUri,
|
|
801920
801862
|
extractKnowledgeArtifact: () => extractKnowledgeArtifact,
|
|
801921
|
-
evaluateProjectPlanningReadiness: () =>
|
|
801922
|
-
discoverBrowserKnowledgeProfiles: () =>
|
|
801863
|
+
evaluateProjectPlanningReadiness: () => evaluateProjectPlanningReadiness2,
|
|
801864
|
+
discoverBrowserKnowledgeProfiles: () => discoverBrowserKnowledgeProfiles2,
|
|
801923
801865
|
createWebKnowledgeGapRepairer: () => createWebKnowledgeGapRepairer,
|
|
801924
801866
|
createProviderBackedKnowledgeSemanticLlm: () => createProviderBackedKnowledgeSemanticLlm,
|
|
801925
801867
|
createMemoryApi: () => createMemoryApi,
|
|
801926
|
-
createKnowledgeSchema: () =>
|
|
801868
|
+
createKnowledgeSchema: () => createSchema3,
|
|
801927
801869
|
createKnowledgeApi: () => createKnowledgeApi,
|
|
801928
801870
|
createDefaultKnowledgeConnectorRegistry: () => createDefaultKnowledgeConnectorRegistry,
|
|
801929
801871
|
compareKnowledgePageSources: () => compareKnowledgePageSources,
|
|
@@ -804387,7 +804329,7 @@ var init_delivery_manager = __esm(() => {
|
|
|
804387
804329
|
});
|
|
804388
804330
|
|
|
804389
804331
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/automation/scheduler-capacity.js
|
|
804390
|
-
function computeSchedulerCapacity(slotsTotal, runs,
|
|
804332
|
+
function computeSchedulerCapacity(slotsTotal, runs, nowMs3 = Date.now()) {
|
|
804391
804333
|
let slotsInUse = 0;
|
|
804392
804334
|
const queuedRuns = [];
|
|
804393
804335
|
for (const run7 of runs) {
|
|
@@ -804397,7 +804339,7 @@ function computeSchedulerCapacity(slotsTotal, runs, nowMs2 = Date.now()) {
|
|
|
804397
804339
|
queuedRuns.push(run7);
|
|
804398
804340
|
}
|
|
804399
804341
|
const queueDepth = queuedRuns.length;
|
|
804400
|
-
const oldestQueuedAgeMs = queueDepth > 0 ?
|
|
804342
|
+
const oldestQueuedAgeMs = queueDepth > 0 ? nowMs3 - Math.min(...queuedRuns.map((r6) => r6.queuedAt)) : null;
|
|
804401
804343
|
return { slotsTotal, slotsInUse, queueDepth, oldestQueuedAgeMs };
|
|
804402
804344
|
}
|
|
804403
804345
|
|
|
@@ -830785,7 +830727,7 @@ var createStyledCell = (char, overrides = {}) => ({
|
|
|
830785
830727
|
// src/version.ts
|
|
830786
830728
|
import { readFileSync } from "fs";
|
|
830787
830729
|
import { join } from "path";
|
|
830788
|
-
var _version = "1.5.
|
|
830730
|
+
var _version = "1.5.9";
|
|
830789
830731
|
try {
|
|
830790
830732
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf-8"));
|
|
830791
830733
|
_version = typeof pkg.version === "string" ? pkg.version : _version;
|
|
@@ -855898,7 +855840,7 @@ var WORK_PLAN_STATUSES = [
|
|
|
855898
855840
|
"failed",
|
|
855899
855841
|
"cancelled"
|
|
855900
855842
|
];
|
|
855901
|
-
function
|
|
855843
|
+
function nowMs3() {
|
|
855902
855844
|
return Date.now();
|
|
855903
855845
|
}
|
|
855904
855846
|
function isObject4(value) {
|
|
@@ -856004,7 +855946,7 @@ class WorkPlanStore {
|
|
|
856004
855946
|
if (!normalizedTitle)
|
|
856005
855947
|
throw new Error("Work plan item title is required.");
|
|
856006
855948
|
const plan = this.readPlan();
|
|
856007
|
-
const time4 =
|
|
855949
|
+
const time4 = nowMs3();
|
|
856008
855950
|
const item = {
|
|
856009
855951
|
id: createItemId(),
|
|
856010
855952
|
title: normalizedTitle,
|
|
@@ -856028,7 +855970,7 @@ class WorkPlanStore {
|
|
|
856028
855970
|
updateItem(idOrPrefix, patch2) {
|
|
856029
855971
|
const plan = this.readPlan();
|
|
856030
855972
|
const item = this.resolveItem(plan, idOrPrefix);
|
|
856031
|
-
const time4 =
|
|
855973
|
+
const time4 = nowMs3();
|
|
856032
855974
|
const nextStatus = patch2.status ?? item.status;
|
|
856033
855975
|
const next = this.pruneItem({
|
|
856034
855976
|
...item,
|
|
@@ -856061,7 +856003,7 @@ class WorkPlanStore {
|
|
|
856061
856003
|
removeItem(idOrPrefix) {
|
|
856062
856004
|
const plan = this.readPlan();
|
|
856063
856005
|
const item = this.resolveItem(plan, idOrPrefix);
|
|
856064
|
-
const time4 =
|
|
856006
|
+
const time4 = nowMs3();
|
|
856065
856007
|
const remaining = plan.items.filter((candidate) => candidate.id !== item.id);
|
|
856066
856008
|
this.writePlan({
|
|
856067
856009
|
...plan,
|
|
@@ -856081,7 +856023,7 @@ class WorkPlanStore {
|
|
|
856081
856023
|
...plan,
|
|
856082
856024
|
items: remaining,
|
|
856083
856025
|
activeItemId: remaining[0]?.id,
|
|
856084
|
-
updatedAt:
|
|
856026
|
+
updatedAt: nowMs3()
|
|
856085
856027
|
});
|
|
856086
856028
|
return removed;
|
|
856087
856029
|
}
|
|
@@ -856120,7 +856062,7 @@ class WorkPlanStore {
|
|
|
856120
856062
|
const parsed = JSON.parse(raw);
|
|
856121
856063
|
if (!isObject4(parsed))
|
|
856122
856064
|
return this.createEmptyPlan();
|
|
856123
|
-
const time4 =
|
|
856065
|
+
const time4 = nowMs3();
|
|
856124
856066
|
const createdAt = typeof parsed.createdAt === "number" ? parsed.createdAt : time4;
|
|
856125
856067
|
const updatedAt = typeof parsed.updatedAt === "number" ? parsed.updatedAt : createdAt;
|
|
856126
856068
|
const items = Array.isArray(parsed.items) ? parsed.items.map((item) => normalizeItem(item, createdAt)).filter((item) => item !== null) : [];
|
|
@@ -856139,7 +856081,7 @@ class WorkPlanStore {
|
|
|
856139
856081
|
};
|
|
856140
856082
|
}
|
|
856141
856083
|
createEmptyPlan() {
|
|
856142
|
-
const time4 =
|
|
856084
|
+
const time4 = nowMs3();
|
|
856143
856085
|
return {
|
|
856144
856086
|
id: createPlanId(this.options.projectId, this.options.projectRoot),
|
|
856145
856087
|
projectId: this.options.projectId,
|
|
@@ -901803,9 +901745,9 @@ Run goodvibes-agent skills discover to inspect available skill files.`, 1);
|
|
|
901803
901745
|
}
|
|
901804
901746
|
|
|
901805
901747
|
// src/cli/memory-command.ts
|
|
901748
|
+
init_state();
|
|
901806
901749
|
import { mkdirSync as mkdirSync83, readFileSync as readFileSync105, writeFileSync as writeFileSync72 } from "fs";
|
|
901807
901750
|
import { dirname as dirname80, resolve as resolve43 } from "path";
|
|
901808
|
-
init_state();
|
|
901809
901751
|
var VALID_CLASSES2 = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
|
|
901810
901752
|
var VALID_SCOPES4 = ["session", "project", "team"];
|
|
901811
901753
|
var VALID_REVIEW_STATES3 = ["fresh", "reviewed", "stale", "contradicted"];
|
|
@@ -901939,10 +901881,7 @@ function timestamp(value) {
|
|
|
901939
901881
|
return new Date(value).toISOString().slice(0, 19).replace("T", " ");
|
|
901940
901882
|
}
|
|
901941
901883
|
function memoryDbPath(runtime2) {
|
|
901942
|
-
return
|
|
901943
|
-
workingDirectory: runtime2.workingDirectory,
|
|
901944
|
-
homeDirectory: runtime2.homeDirectory
|
|
901945
|
-
}).resolveUserPath(GOODVIBES_AGENT_SURFACE_ROOT, "memory.sqlite");
|
|
901884
|
+
return resolveCanonicalMemoryDbPath(runtime2.homeDirectory);
|
|
901946
901885
|
}
|
|
901947
901886
|
async function withMemory(runtime2, fn) {
|
|
901948
901887
|
const path7 = memoryDbPath(runtime2);
|
|
@@ -967154,7 +967093,7 @@ class LocalStreamingAudioPlayer {
|
|
|
967154
967093
|
activeProcess = null;
|
|
967155
967094
|
spawnProcess;
|
|
967156
967095
|
constructor(options = {}) {
|
|
967157
|
-
this.command = resolveStreamingAudioPlayerCommand(options.env ?? process.env);
|
|
967096
|
+
this.command = options.command !== undefined ? options.command : resolveStreamingAudioPlayerCommand(options.env ?? process.env);
|
|
967158
967097
|
this.spawnProcess = options.spawnProcess ?? defaultSpawnProcess;
|
|
967159
967098
|
}
|
|
967160
967099
|
get label() {
|
|
@@ -967181,6 +967120,9 @@ class LocalStreamingAudioPlayer {
|
|
|
967181
967120
|
};
|
|
967182
967121
|
options.signal?.addEventListener("abort", abort6, { once: true });
|
|
967183
967122
|
try {
|
|
967123
|
+
await awaitReady(proc, options.signal);
|
|
967124
|
+
if (options.signal?.aborted)
|
|
967125
|
+
return;
|
|
967184
967126
|
for await (const chunk of chunks) {
|
|
967185
967127
|
if (options.signal?.aborted)
|
|
967186
967128
|
break;
|
|
@@ -967188,7 +967130,11 @@ class LocalStreamingAudioPlayer {
|
|
|
967188
967130
|
continue;
|
|
967189
967131
|
await writeStdin(proc, chunk.data);
|
|
967190
967132
|
}
|
|
967191
|
-
|
|
967133
|
+
if (options.signal?.aborted)
|
|
967134
|
+
return;
|
|
967135
|
+
try {
|
|
967136
|
+
proc.stdin.end();
|
|
967137
|
+
} catch {}
|
|
967192
967138
|
await waitForExit(proc);
|
|
967193
967139
|
} finally {
|
|
967194
967140
|
options.signal?.removeEventListener("abort", abort6);
|
|
@@ -967208,13 +967154,30 @@ class LocalStreamingAudioPlayer {
|
|
|
967208
967154
|
proc.kill("SIGTERM");
|
|
967209
967155
|
} catch {}
|
|
967210
967156
|
}
|
|
967157
|
+
waitForDrain(timeoutMs) {
|
|
967158
|
+
const proc = this.activeProcess;
|
|
967159
|
+
if (!proc)
|
|
967160
|
+
return Promise.resolve();
|
|
967161
|
+
return new Promise((resolve45) => {
|
|
967162
|
+
let settled = false;
|
|
967163
|
+
const settle = () => {
|
|
967164
|
+
if (settled)
|
|
967165
|
+
return;
|
|
967166
|
+
settled = true;
|
|
967167
|
+
clearTimeout(timer);
|
|
967168
|
+
resolve45();
|
|
967169
|
+
};
|
|
967170
|
+
const timer = setTimeout(settle, timeoutMs);
|
|
967171
|
+
proc.once("close", settle);
|
|
967172
|
+
});
|
|
967173
|
+
}
|
|
967211
967174
|
}
|
|
967212
967175
|
function resolveStreamingAudioPlayerCommand(env2 = process.env) {
|
|
967213
967176
|
const mpv = findExecutable("mpv", env2);
|
|
967214
967177
|
if (mpv) {
|
|
967215
967178
|
return {
|
|
967216
967179
|
command: mpv,
|
|
967217
|
-
args: ["--no-terminal", "--really-quiet", "--force-window=no", "
|
|
967180
|
+
args: ["--no-terminal", "--really-quiet", "--force-window=no", "-"],
|
|
967218
967181
|
label: "mpv"
|
|
967219
967182
|
};
|
|
967220
967183
|
}
|
|
@@ -967222,19 +967185,41 @@ function resolveStreamingAudioPlayerCommand(env2 = process.env) {
|
|
|
967222
967185
|
if (ffplay) {
|
|
967223
967186
|
return {
|
|
967224
967187
|
command: ffplay,
|
|
967225
|
-
args:
|
|
967188
|
+
args: FFPLAY_BASE_ARGS,
|
|
967226
967189
|
label: "ffplay"
|
|
967227
967190
|
};
|
|
967228
967191
|
}
|
|
967229
967192
|
return null;
|
|
967230
967193
|
}
|
|
967194
|
+
var FFPLAY_APAD = ["-af", "apad=pad_dur=0.3"];
|
|
967195
|
+
var FFPLAY_BASE_ARGS = ["-nodisp", "-autoexit", "-loglevel", "error", ...FFPLAY_APAD, "-i", "pipe:0"];
|
|
967231
967196
|
function buildPlayerArgs(command8, format3) {
|
|
967232
967197
|
if (command8.label !== "ffplay" || !format3)
|
|
967233
967198
|
return command8.args;
|
|
967234
967199
|
const normalized3 = format3.trim().toLowerCase();
|
|
967235
967200
|
if (!normalized3 || normalized3.includes("/"))
|
|
967236
967201
|
return command8.args;
|
|
967237
|
-
return ["-nodisp", "-autoexit", "-loglevel", "error", "-f", normalized3, "-i", "pipe:0"];
|
|
967202
|
+
return ["-nodisp", "-autoexit", "-loglevel", "error", ...FFPLAY_APAD, "-f", normalized3, "-i", "pipe:0"];
|
|
967203
|
+
}
|
|
967204
|
+
function awaitReady(proc, signal) {
|
|
967205
|
+
if (signal?.aborted)
|
|
967206
|
+
return Promise.resolve();
|
|
967207
|
+
return new Promise((resolve45, reject) => {
|
|
967208
|
+
let settled = false;
|
|
967209
|
+
const settle = (action2) => {
|
|
967210
|
+
if (settled)
|
|
967211
|
+
return;
|
|
967212
|
+
settled = true;
|
|
967213
|
+
signal?.removeEventListener("abort", onAbort);
|
|
967214
|
+
action2();
|
|
967215
|
+
};
|
|
967216
|
+
const onSpawn = () => settle(resolve45);
|
|
967217
|
+
const onError2 = (error73) => settle(() => reject(error73 instanceof Error ? error73 : new Error(String(error73))));
|
|
967218
|
+
const onAbort = () => settle(resolve45);
|
|
967219
|
+
proc.once("spawn", onSpawn);
|
|
967220
|
+
proc.once("error", onError2);
|
|
967221
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
967222
|
+
});
|
|
967238
967223
|
}
|
|
967239
967224
|
function findExecutable(name51, env2) {
|
|
967240
967225
|
const pathValue = env2.PATH ?? "";
|
|
@@ -967372,6 +967357,10 @@ function normalizeSpeechText(text) {
|
|
|
967372
967357
|
}
|
|
967373
967358
|
|
|
967374
967359
|
// src/audio/spoken-turn-controller.ts
|
|
967360
|
+
var SYNTHESIS_PIPELINE_WINDOW = 2;
|
|
967361
|
+
var SYNTHESIS_MERGE_MAX_CHARS = 1500;
|
|
967362
|
+
var SYNTHESIS_RETRY_DELAYS_MS = [1000, 2500];
|
|
967363
|
+
|
|
967375
967364
|
class SpokenTurnController {
|
|
967376
967365
|
pendingPrompt = null;
|
|
967377
967366
|
activeTurnId = null;
|
|
@@ -967381,6 +967370,12 @@ class SpokenTurnController {
|
|
|
967381
967370
|
abortControllers = new Set;
|
|
967382
967371
|
timer = null;
|
|
967383
967372
|
errorReportedForTurn = false;
|
|
967373
|
+
noPlayerNoticed = false;
|
|
967374
|
+
pendingTexts = [];
|
|
967375
|
+
pipelineDepth = 0;
|
|
967376
|
+
pipelineGeneration = 0;
|
|
967377
|
+
pumpScheduled = false;
|
|
967378
|
+
completedTurnId = null;
|
|
967384
967379
|
voiceService;
|
|
967385
967380
|
configManager;
|
|
967386
967381
|
player;
|
|
@@ -967388,6 +967383,8 @@ class SpokenTurnController {
|
|
|
967388
967383
|
now;
|
|
967389
967384
|
setIntervalImpl;
|
|
967390
967385
|
clearIntervalImpl;
|
|
967386
|
+
setTimeoutImpl;
|
|
967387
|
+
clearTimeoutImpl;
|
|
967391
967388
|
constructor(options) {
|
|
967392
967389
|
this.voiceService = options.voiceService;
|
|
967393
967390
|
this.configManager = options.configManager;
|
|
@@ -967396,6 +967393,8 @@ class SpokenTurnController {
|
|
|
967396
967393
|
this.now = options.now ?? (() => Date.now());
|
|
967397
967394
|
this.setIntervalImpl = options.setInterval ?? setInterval;
|
|
967398
967395
|
this.clearIntervalImpl = options.clearInterval ?? clearInterval;
|
|
967396
|
+
this.setTimeoutImpl = options.setTimeout ?? setTimeout;
|
|
967397
|
+
this.clearTimeoutImpl = options.clearTimeout ?? clearTimeout;
|
|
967399
967398
|
}
|
|
967400
967399
|
submitNextTurn(prompt) {
|
|
967401
967400
|
const normalized3 = prompt.trim();
|
|
@@ -967403,26 +967402,46 @@ class SpokenTurnController {
|
|
|
967403
967402
|
return false;
|
|
967404
967403
|
this.stop();
|
|
967405
967404
|
if (!this.player.available) {
|
|
967406
|
-
this.
|
|
967405
|
+
if (!this.noPlayerNoticed) {
|
|
967406
|
+
this.noPlayerNoticed = true;
|
|
967407
|
+
this.notify?.("[TTS] Text response will continue, but live audio is unavailable. Install mpv or ffplay.");
|
|
967408
|
+
}
|
|
967407
967409
|
return false;
|
|
967408
967410
|
}
|
|
967411
|
+
this.noPlayerNoticed = false;
|
|
967409
967412
|
this.pendingPrompt = normalized3;
|
|
967410
967413
|
return true;
|
|
967411
967414
|
}
|
|
967412
967415
|
stop(message) {
|
|
967416
|
+
const wasActive = this.pendingPrompt !== null || this.activeTurnId !== null || this.chunker !== null || this.abortControllers.size > 0;
|
|
967413
967417
|
this.pendingPrompt = null;
|
|
967414
967418
|
this.activeTurnId = null;
|
|
967415
967419
|
this.chunker?.reset();
|
|
967416
967420
|
this.chunker = null;
|
|
967417
967421
|
this.stopTimer();
|
|
967422
|
+
this.resetPipeline();
|
|
967418
967423
|
for (const controller of this.abortControllers)
|
|
967419
967424
|
controller.abort();
|
|
967420
967425
|
this.abortControllers.clear();
|
|
967421
967426
|
this.player.stop();
|
|
967422
967427
|
this.playbackChain = Promise.resolve();
|
|
967423
967428
|
this.errorReportedForTurn = false;
|
|
967424
|
-
if (message)
|
|
967429
|
+
if (message && wasActive)
|
|
967425
967430
|
this.notify?.(`[TTS] ${message}`);
|
|
967431
|
+
return wasActive;
|
|
967432
|
+
}
|
|
967433
|
+
async stopForExit(drainTimeoutMs = 2000) {
|
|
967434
|
+
this.pendingPrompt = null;
|
|
967435
|
+
this.activeTurnId = null;
|
|
967436
|
+
this.chunker?.reset();
|
|
967437
|
+
this.chunker = null;
|
|
967438
|
+
this.stopTimer();
|
|
967439
|
+
this.resetPipeline();
|
|
967440
|
+
for (const controller of this.abortControllers)
|
|
967441
|
+
controller.abort();
|
|
967442
|
+
this.abortControllers.clear();
|
|
967443
|
+
await this.player.waitForDrain(drainTimeoutMs);
|
|
967444
|
+
this.stop();
|
|
967426
967445
|
}
|
|
967427
967446
|
handleTurnEvent(event) {
|
|
967428
967447
|
if (event.type === "TURN_SUBMITTED") {
|
|
@@ -967432,7 +967451,7 @@ class SpokenTurnController {
|
|
|
967432
967451
|
if (!this.activeTurnId || event.turnId !== this.activeTurnId)
|
|
967433
967452
|
return;
|
|
967434
967453
|
if (event.type === "STREAM_DELTA") {
|
|
967435
|
-
this.
|
|
967454
|
+
this.queueTexts(this.chunker?.push(event.content) ?? []);
|
|
967436
967455
|
return;
|
|
967437
967456
|
}
|
|
967438
967457
|
if (event.type === "STREAM_END") {
|
|
@@ -967457,29 +967476,30 @@ class SpokenTurnController {
|
|
|
967457
967476
|
this.errorReportedForTurn = false;
|
|
967458
967477
|
this.chunker = new TtsTextChunker({ now: this.now });
|
|
967459
967478
|
this.playbackChain = Promise.resolve();
|
|
967479
|
+
this.resetPipeline();
|
|
967460
967480
|
this.startTimer();
|
|
967461
967481
|
this.notify?.(`[TTS] Live playback queued through ${this.player.label}.`);
|
|
967462
967482
|
}
|
|
967463
967483
|
finishTurn(turnId) {
|
|
967464
967484
|
if (turnId !== this.activeTurnId)
|
|
967465
967485
|
return;
|
|
967466
|
-
this.
|
|
967486
|
+
this.queueTexts(this.chunker?.flushAll() ?? []);
|
|
967467
967487
|
this.stopTimer();
|
|
967468
|
-
|
|
967469
|
-
|
|
967470
|
-
|
|
967471
|
-
|
|
967472
|
-
|
|
967473
|
-
|
|
967474
|
-
|
|
967475
|
-
|
|
967488
|
+
this.completedTurnId = turnId;
|
|
967489
|
+
this.maybeReleaseTurn();
|
|
967490
|
+
}
|
|
967491
|
+
resetPipeline() {
|
|
967492
|
+
this.pendingTexts = [];
|
|
967493
|
+
this.pipelineDepth = 0;
|
|
967494
|
+
this.pipelineGeneration++;
|
|
967495
|
+
this.completedTurnId = null;
|
|
967476
967496
|
}
|
|
967477
967497
|
startTimer() {
|
|
967478
967498
|
this.stopTimer();
|
|
967479
967499
|
this.timer = this.setIntervalImpl(() => {
|
|
967480
967500
|
if (!this.activeTurnId || !this.chunker)
|
|
967481
967501
|
return;
|
|
967482
|
-
this.
|
|
967502
|
+
this.queueTexts(this.chunker.flushDue());
|
|
967483
967503
|
}, 250);
|
|
967484
967504
|
}
|
|
967485
967505
|
stopTimer() {
|
|
@@ -967488,42 +967508,136 @@ class SpokenTurnController {
|
|
|
967488
967508
|
this.clearIntervalImpl(this.timer);
|
|
967489
967509
|
this.timer = null;
|
|
967490
967510
|
}
|
|
967491
|
-
|
|
967511
|
+
queueTexts(chunks) {
|
|
967492
967512
|
for (const chunk of chunks) {
|
|
967493
|
-
|
|
967513
|
+
if (chunk.trim())
|
|
967514
|
+
this.pendingTexts.push(chunk);
|
|
967515
|
+
}
|
|
967516
|
+
if (this.pendingTexts.length > 0)
|
|
967517
|
+
this.schedulePump();
|
|
967518
|
+
}
|
|
967519
|
+
schedulePump() {
|
|
967520
|
+
if (this.pumpScheduled)
|
|
967521
|
+
return;
|
|
967522
|
+
this.pumpScheduled = true;
|
|
967523
|
+
queueMicrotask(() => {
|
|
967524
|
+
this.pumpScheduled = false;
|
|
967525
|
+
this.pump();
|
|
967526
|
+
});
|
|
967527
|
+
}
|
|
967528
|
+
pump() {
|
|
967529
|
+
while (this.activeTurnId && this.pendingTexts.length > 0 && this.pipelineDepth < SYNTHESIS_PIPELINE_WINDOW) {
|
|
967530
|
+
this.dispatchChunk(this.takeMergedText());
|
|
967494
967531
|
}
|
|
967532
|
+
this.maybeReleaseTurn();
|
|
967495
967533
|
}
|
|
967496
|
-
|
|
967534
|
+
takeMergedText() {
|
|
967535
|
+
let merged = "";
|
|
967536
|
+
while (this.pendingTexts.length > 0) {
|
|
967537
|
+
const next = this.pendingTexts[0];
|
|
967538
|
+
if (!merged && next.length > SYNTHESIS_MERGE_MAX_CHARS) {
|
|
967539
|
+
const cut = findSplitIndex(next, SYNTHESIS_MERGE_MAX_CHARS);
|
|
967540
|
+
this.pendingTexts[0] = next.slice(cut).trim();
|
|
967541
|
+
return next.slice(0, cut).trim();
|
|
967542
|
+
}
|
|
967543
|
+
if (merged && merged.length + 1 + next.length > SYNTHESIS_MERGE_MAX_CHARS)
|
|
967544
|
+
break;
|
|
967545
|
+
merged = merged ? `${merged} ${next}` : next;
|
|
967546
|
+
this.pendingTexts.shift();
|
|
967547
|
+
}
|
|
967548
|
+
return merged;
|
|
967549
|
+
}
|
|
967550
|
+
dispatchChunk(text) {
|
|
967497
967551
|
const turnId = this.activeTurnId;
|
|
967498
967552
|
if (!turnId || !text.trim())
|
|
967499
967553
|
return;
|
|
967500
967554
|
const sequence = ++this.chunkSequence;
|
|
967555
|
+
const generation = this.pipelineGeneration;
|
|
967556
|
+
this.pipelineDepth++;
|
|
967501
967557
|
const abortController = new AbortController;
|
|
967502
967558
|
this.abortControllers.add(abortController);
|
|
967503
|
-
const resultPromise = this.
|
|
967559
|
+
const resultPromise = this.synthesizeWithRetry(text, turnId, sequence, abortController.signal).then((result2) => ({ ok: true, result: result2 })).catch((error73) => ({ ok: false, error: error73 }));
|
|
967504
967560
|
this.playbackChain = this.playbackChain.then(async () => {
|
|
967505
|
-
|
|
967506
|
-
|
|
967507
|
-
|
|
967508
|
-
|
|
967509
|
-
|
|
967510
|
-
|
|
967511
|
-
|
|
967561
|
+
try {
|
|
967562
|
+
if (abortController.signal.aborted) {
|
|
967563
|
+
this.abortControllers.delete(abortController);
|
|
967564
|
+
return;
|
|
967565
|
+
}
|
|
967566
|
+
const result2 = await resultPromise;
|
|
967567
|
+
this.abortControllers.delete(abortController);
|
|
967568
|
+
if (abortController.signal.aborted)
|
|
967569
|
+
return;
|
|
967570
|
+
if (!result2.ok) {
|
|
967571
|
+
this.reportSkippedChunk(result2.error);
|
|
967572
|
+
return;
|
|
967573
|
+
}
|
|
967574
|
+
await this.player.play(result2.result.chunks, {
|
|
967575
|
+
format: String(result2.result.format ?? "mp3"),
|
|
967576
|
+
signal: abortController.signal
|
|
967577
|
+
});
|
|
967578
|
+
} finally {
|
|
967579
|
+
this.releasePipelineSlot(generation);
|
|
967512
967580
|
}
|
|
967513
|
-
await this.player.play(result2.result.chunks, {
|
|
967514
|
-
format: String(result2.result.format ?? "mp3"),
|
|
967515
|
-
signal: abortController.signal
|
|
967516
|
-
});
|
|
967517
967581
|
}).catch((error73) => {
|
|
967518
967582
|
this.abortControllers.delete(abortController);
|
|
967519
967583
|
this.reportError(error73);
|
|
967520
967584
|
});
|
|
967521
967585
|
}
|
|
967586
|
+
releasePipelineSlot(generation) {
|
|
967587
|
+
if (generation !== this.pipelineGeneration)
|
|
967588
|
+
return;
|
|
967589
|
+
this.pipelineDepth = Math.max(0, this.pipelineDepth - 1);
|
|
967590
|
+
if (this.pendingTexts.length > 0) {
|
|
967591
|
+
this.schedulePump();
|
|
967592
|
+
return;
|
|
967593
|
+
}
|
|
967594
|
+
this.maybeReleaseTurn();
|
|
967595
|
+
}
|
|
967596
|
+
maybeReleaseTurn() {
|
|
967597
|
+
if (!this.completedTurnId || this.completedTurnId !== this.activeTurnId)
|
|
967598
|
+
return;
|
|
967599
|
+
if (this.pendingTexts.length > 0 || this.pipelineDepth > 0 || this.pumpScheduled)
|
|
967600
|
+
return;
|
|
967601
|
+
this.activeTurnId = null;
|
|
967602
|
+
this.completedTurnId = null;
|
|
967603
|
+
this.chunker = null;
|
|
967604
|
+
this.abortControllers.clear();
|
|
967605
|
+
}
|
|
967606
|
+
async synthesizeWithRetry(text, turnId, sequence, signal) {
|
|
967607
|
+
for (let attempt = 0;; attempt++) {
|
|
967608
|
+
try {
|
|
967609
|
+
return await this.synthesize(text, turnId, sequence, signal);
|
|
967610
|
+
} catch (error73) {
|
|
967611
|
+
const retryable = attempt < SYNTHESIS_RETRY_DELAYS_MS.length && !signal.aborted && isTransientSynthesisError(error73);
|
|
967612
|
+
if (!retryable)
|
|
967613
|
+
throw error73;
|
|
967614
|
+
await this.delay(SYNTHESIS_RETRY_DELAYS_MS[attempt], signal);
|
|
967615
|
+
}
|
|
967616
|
+
}
|
|
967617
|
+
}
|
|
967618
|
+
delay(ms, signal) {
|
|
967619
|
+
return new Promise((resolve45, reject) => {
|
|
967620
|
+
if (signal.aborted) {
|
|
967621
|
+
reject(new Error("Synthesis retry cancelled"));
|
|
967622
|
+
return;
|
|
967623
|
+
}
|
|
967624
|
+
const timer = this.setTimeoutImpl(() => {
|
|
967625
|
+
signal.removeEventListener("abort", onAbort);
|
|
967626
|
+
resolve45();
|
|
967627
|
+
}, ms);
|
|
967628
|
+
const onAbort = () => {
|
|
967629
|
+
this.clearTimeoutImpl(timer);
|
|
967630
|
+
reject(new Error("Synthesis retry cancelled"));
|
|
967631
|
+
};
|
|
967632
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
967633
|
+
});
|
|
967634
|
+
}
|
|
967522
967635
|
synthesize(text, turnId, sequence, signal) {
|
|
967523
967636
|
return this.voiceService.synthesizeStream(readOptionalConfigString2(this.configManager, "tts.provider"), {
|
|
967524
967637
|
text,
|
|
967525
967638
|
voiceId: readOptionalConfigString2(this.configManager, "tts.voice"),
|
|
967526
967639
|
format: "mp3",
|
|
967640
|
+
speed: readOptionalConfigNumber(this.configManager, "tts.speed"),
|
|
967527
967641
|
signal,
|
|
967528
967642
|
metadata: {
|
|
967529
967643
|
source: "goodvibes-agent",
|
|
@@ -967533,6 +967647,12 @@ class SpokenTurnController {
|
|
|
967533
967647
|
}
|
|
967534
967648
|
});
|
|
967535
967649
|
}
|
|
967650
|
+
reportSkippedChunk(error73) {
|
|
967651
|
+
if (this.errorReportedForTurn)
|
|
967652
|
+
return;
|
|
967653
|
+
this.errorReportedForTurn = true;
|
|
967654
|
+
this.notify?.(`[TTS] Skipping part of the spoken response \u2014 synthesis kept failing (${summarizeError(error73)}). Playback continues with the rest.`);
|
|
967655
|
+
}
|
|
967536
967656
|
reportError(error73) {
|
|
967537
967657
|
if (this.errorReportedForTurn)
|
|
967538
967658
|
return;
|
|
@@ -967540,6 +967660,7 @@ class SpokenTurnController {
|
|
|
967540
967660
|
this.activeTurnId = null;
|
|
967541
967661
|
this.chunker = null;
|
|
967542
967662
|
this.stopTimer();
|
|
967663
|
+
this.resetPipeline();
|
|
967543
967664
|
for (const controller of this.abortControllers)
|
|
967544
967665
|
controller.abort();
|
|
967545
967666
|
this.abortControllers.clear();
|
|
@@ -967548,17 +967669,35 @@ class SpokenTurnController {
|
|
|
967548
967669
|
this.notify?.(`[TTS] Live playback stopped: ${summarizeError(error73)}`);
|
|
967549
967670
|
}
|
|
967550
967671
|
}
|
|
967672
|
+
function isTransientSynthesisError(error73) {
|
|
967673
|
+
const message = (error73 instanceof Error ? error73.message : String(error73)).toLowerCase();
|
|
967674
|
+
if (message.includes("429") || message.includes("rate limit") || message.includes("rate_limit") || message.includes("too many requests") || message.includes("concurrent"))
|
|
967675
|
+
return true;
|
|
967676
|
+
if (/http 5\d\d/.test(message))
|
|
967677
|
+
return true;
|
|
967678
|
+
return message.includes("fetch failed") || message.includes("network") || message.includes("timed out") || message.includes("timeout") || message.includes("econnreset") || message.includes("socket");
|
|
967679
|
+
}
|
|
967680
|
+
function findSplitIndex(text, limit3) {
|
|
967681
|
+
const space = text.lastIndexOf(" ", limit3);
|
|
967682
|
+
return space > 0 ? space : limit3;
|
|
967683
|
+
}
|
|
967551
967684
|
function readOptionalConfigString2(configManager, key) {
|
|
967552
967685
|
const value = String(configManager.get(key) ?? "").trim();
|
|
967553
967686
|
return value || undefined;
|
|
967554
967687
|
}
|
|
967688
|
+
function readOptionalConfigNumber(configManager, key) {
|
|
967689
|
+
const raw = configManager.get(key);
|
|
967690
|
+
const value = typeof raw === "number" ? raw : parseFloat(String(raw ?? ""));
|
|
967691
|
+
return isFinite(value) && value > 0 ? value : undefined;
|
|
967692
|
+
}
|
|
967555
967693
|
|
|
967556
967694
|
// src/audio/spoken-turn-wiring.ts
|
|
967557
967695
|
function wireSpokenTurnRuntime(options) {
|
|
967696
|
+
const player = options.playerFactory ? options.playerFactory() : new LocalStreamingAudioPlayer;
|
|
967558
967697
|
const controller = new SpokenTurnController({
|
|
967559
967698
|
voiceService: options.voiceService,
|
|
967560
967699
|
configManager: options.configManager,
|
|
967561
|
-
player
|
|
967700
|
+
player,
|
|
967562
967701
|
notify: options.notify
|
|
967563
967702
|
});
|
|
967564
967703
|
const turns = options.events.turns;
|
|
@@ -967574,7 +967713,35 @@ function wireSpokenTurnRuntime(options) {
|
|
|
967574
967713
|
return {
|
|
967575
967714
|
unsubs,
|
|
967576
967715
|
submitNextTurn: (prompt) => controller.submitNextTurn(prompt),
|
|
967577
|
-
stop: (message) => controller.stop(message)
|
|
967716
|
+
stop: (message) => controller.stop(message),
|
|
967717
|
+
stopForExit: (drainTimeoutMs) => controller.stopForExit(drainTimeoutMs)
|
|
967718
|
+
};
|
|
967719
|
+
}
|
|
967720
|
+
|
|
967721
|
+
// src/runtime/unhandled-rejection-guard.ts
|
|
967722
|
+
function createUnhandledRejectionHandler(deps) {
|
|
967723
|
+
let rejectionCount = 0;
|
|
967724
|
+
let windowStart = Date.now();
|
|
967725
|
+
return (reason) => {
|
|
967726
|
+
const now5 = Date.now();
|
|
967727
|
+
if (now5 - windowStart > 1e4) {
|
|
967728
|
+
rejectionCount = 0;
|
|
967729
|
+
windowStart = now5;
|
|
967730
|
+
}
|
|
967731
|
+
rejectionCount++;
|
|
967732
|
+
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
967733
|
+
if (rejectionCount > 3) {
|
|
967734
|
+
logger.error("CRITICAL: cascading unhandled rejections \u2014 consider restarting", {
|
|
967735
|
+
count: rejectionCount,
|
|
967736
|
+
windowMs: now5 - windowStart,
|
|
967737
|
+
error: String(reason)
|
|
967738
|
+
});
|
|
967739
|
+
deps.notifyHigh(`[Critical] Multiple errors detected (${rejectionCount} in 10s). If the issue persists, please restart. Latest: ${msg}`);
|
|
967740
|
+
} else {
|
|
967741
|
+
deps.notifyHigh(`[Error] ${msg}`);
|
|
967742
|
+
logger.error("unhandledRejection", { error: String(reason) });
|
|
967743
|
+
}
|
|
967744
|
+
deps.render();
|
|
967578
967745
|
};
|
|
967579
967746
|
}
|
|
967580
967747
|
|
|
@@ -967711,8 +967878,8 @@ function formatDigestTime(at, from = Date.now()) {
|
|
|
967711
967878
|
return `yesterday ${time4}`;
|
|
967712
967879
|
return `${d4.toLocaleDateString(undefined, { month: "short", day: "numeric" })} ${time4}`;
|
|
967713
967880
|
}
|
|
967714
|
-
function formatRelativeTime(targetMs,
|
|
967715
|
-
const diffMs = targetMs -
|
|
967881
|
+
function formatRelativeTime(targetMs, nowMs4 = Date.now()) {
|
|
967882
|
+
const diffMs = targetMs - nowMs4;
|
|
967716
967883
|
if (diffMs <= 0)
|
|
967717
967884
|
return "soon";
|
|
967718
967885
|
const diffMin = Math.round(diffMs / 60000);
|
|
@@ -968104,36 +968271,26 @@ async function main() {
|
|
|
968104
968271
|
let stopSpokenOutputForExit = null;
|
|
968105
968272
|
let recoveryPending = false;
|
|
968106
968273
|
const sigintHandler = () => input.feed("\x03");
|
|
968107
|
-
|
|
968108
|
-
|
|
968109
|
-
|
|
968110
|
-
|
|
968111
|
-
if (now5 - _unhandledRejectionWindowStart > 1e4) {
|
|
968112
|
-
_unhandledRejectionCount = 0;
|
|
968113
|
-
_unhandledRejectionWindowStart = now5;
|
|
968114
|
-
}
|
|
968115
|
-
_unhandledRejectionCount++;
|
|
968116
|
-
const msg = reason instanceof Error ? reason.message : String(reason);
|
|
968117
|
-
if (_unhandledRejectionCount > 3) {
|
|
968118
|
-
logger.error("CRITICAL: cascading unhandled rejections \u2014 consider restarting", {
|
|
968119
|
-
count: _unhandledRejectionCount,
|
|
968120
|
-
windowMs: now5 - _unhandledRejectionWindowStart,
|
|
968121
|
-
error: String(reason)
|
|
968122
|
-
});
|
|
968123
|
-
systemMessageRouter.high(`[Critical] Multiple errors detected (${_unhandledRejectionCount} in 10s). If the issue persists, please restart. Latest: ${msg}`);
|
|
968124
|
-
} else {
|
|
968125
|
-
systemMessageRouter.high(`[Error] ${msg}`);
|
|
968126
|
-
logger.error("unhandledRejection", { error: String(reason) });
|
|
968127
|
-
}
|
|
968128
|
-
render();
|
|
968129
|
-
};
|
|
968274
|
+
const unhandledRejectionHandler = createUnhandledRejectionHandler({
|
|
968275
|
+
notifyHigh: (message) => systemMessageRouter.high(message),
|
|
968276
|
+
render: () => render()
|
|
968277
|
+
});
|
|
968130
968278
|
const resizeHandler = () => {
|
|
968131
968279
|
input.setContentWidth(getPromptContentWidth());
|
|
968132
968280
|
compositor.resetDiff();
|
|
968133
968281
|
render();
|
|
968134
968282
|
};
|
|
968283
|
+
let exiting = false;
|
|
968135
968284
|
const exitApp = () => {
|
|
968136
|
-
|
|
968285
|
+
if (exiting)
|
|
968286
|
+
return;
|
|
968287
|
+
exiting = true;
|
|
968288
|
+
let spokenOutputDrain = Promise.resolve();
|
|
968289
|
+
try {
|
|
968290
|
+
spokenOutputDrain = Promise.resolve(stopSpokenOutputForExit?.()).then(() => {
|
|
968291
|
+
return;
|
|
968292
|
+
});
|
|
968293
|
+
} catch {}
|
|
968137
968294
|
unsubs.forEach((fn) => fn());
|
|
968138
968295
|
autonomy.stop();
|
|
968139
968296
|
const snapshot2 = buildCurrentSessionSnapshot();
|
|
@@ -968153,7 +968310,9 @@ async function main() {
|
|
|
968153
968310
|
allowTerminalWrite(() => stdout.write(PASTE_DISABLE + KEYBOARD_EXT_DISABLE2 + MOUSE_DISABLE + FOCUS_DISABLE + CURSOR_SHOW + exitScreen));
|
|
968154
968311
|
terminalOutputGuard.dispose();
|
|
968155
968312
|
stdin.setRawMode(false);
|
|
968156
|
-
|
|
968313
|
+
spokenOutputDrain.catch(() => {
|
|
968314
|
+
return;
|
|
968315
|
+
}).then(() => process.exit(0));
|
|
968157
968316
|
};
|
|
968158
968317
|
commandContext.exit = exitApp;
|
|
968159
968318
|
const spokenTurns = wireSpokenTurnRuntime({
|
|
@@ -968165,7 +968324,7 @@ async function main() {
|
|
|
968165
968324
|
render();
|
|
968166
968325
|
}
|
|
968167
968326
|
});
|
|
968168
|
-
stopSpokenOutputForExit = () => spokenTurns.
|
|
968327
|
+
stopSpokenOutputForExit = () => spokenTurns.stopForExit();
|
|
968169
968328
|
unsubs.push(...spokenTurns.unsubs);
|
|
968170
968329
|
unsubs.push(attachSpokenTurnModelRouting({
|
|
968171
968330
|
orchestrator,
|