@pellux/goodvibes-agent 1.6.1 → 1.6.3
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 +8 -0
- package/README.md +1 -1
- package/dist/package/main.js +566 -70
- package/package.json +2 -2
- package/src/tools/agent-harness-background-processes.ts +3 -2
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Product-facing release notes for GoodVibes Agent.
|
|
4
4
|
|
|
5
|
+
## 1.6.3 - 2026-07-07
|
|
6
|
+
|
|
7
|
+
- SDK 1.4.0 pin: the shared daemon contracts gain server-side companion-chat turn control — a true stop verb (provider stream aborted, honest partial persisted), queue-when-busy sends, and steer (interrupt-and-send-now). No agent-side behavior changes; agent session turns already had lifecycle control on the operator wire.
|
|
8
|
+
|
|
9
|
+
## 1.6.2 - 2026-07-07
|
|
10
|
+
|
|
11
|
+
- Updated to SDK 1.3.3: the macOS capability-limit classification now matches Apple SQLite's exact refusal message, so compiled binaries report the honest limit instead of an error.
|
|
12
|
+
|
|
5
13
|
## 1.6.1 - 2026-07-07
|
|
6
14
|
|
|
7
15
|
- Updated to SDK 1.3.2: a platform that cannot load SQLite extensions (macOS system SQLite in a compiled binary) now reports an honest capability limit instead of an error; semantic memory search states its literal fallback.
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# GoodVibes Agent
|
|
2
2
|
|
|
3
3
|
[](https://opensource.org/licenses/MIT)
|
|
4
|
-
[](#install)
|
|
5
5
|
|
|
6
6
|
GoodVibes Agent is the installable autonomous operator assistant for GoodVibes. It keeps the existing terminal renderer and workspace bones, but the product goal is different from a vibecoding harness: the user should experience one assistant that can chat, plan, remember, research, schedule, send, generate, run visible agents, and operate the GoodVibes daemon contract with clear confirmation gates.
|
|
7
7
|
|
package/dist/package/main.js
CHANGED
|
@@ -4463,7 +4463,7 @@ var init_mcp = __esm(() => {
|
|
|
4463
4463
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/version.js
|
|
4464
4464
|
import { readFileSync as readFileSync3 } from "fs";
|
|
4465
4465
|
import { join as join4 } from "path";
|
|
4466
|
-
var version = "1.
|
|
4466
|
+
var version = "1.4.0", VERSION2;
|
|
4467
4467
|
var init_version = __esm(() => {
|
|
4468
4468
|
try {
|
|
4469
4469
|
const pkg = JSON.parse(readFileSync3(join4(import.meta.dir, "..", "..", "package.json"), "utf-8"));
|
|
@@ -45977,7 +45977,7 @@ function resolveSqliteVecPath() {
|
|
|
45977
45977
|
}
|
|
45978
45978
|
function isExtensionLoadingRefusal(err2) {
|
|
45979
45979
|
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
45980
|
-
return /not authorized|omit.*load.*extension|extension loading is disabled/i.test(message);
|
|
45980
|
+
return /not authorized|omit.*load.*extension|extension loading is disabled|does not support dynamic extension loading/i.test(message);
|
|
45981
45981
|
}
|
|
45982
45982
|
function loadSqliteVecExtension(db) {
|
|
45983
45983
|
const bundledPath = resolveSqliteVecPath();
|
|
@@ -264004,8 +264004,8 @@ var FOUNDATION_METADATA;
|
|
|
264004
264004
|
var init_foundation_metadata = __esm(() => {
|
|
264005
264005
|
FOUNDATION_METADATA = {
|
|
264006
264006
|
productId: "goodvibes",
|
|
264007
|
-
productVersion: "1.
|
|
264008
|
-
operatorMethodCount:
|
|
264007
|
+
productVersion: "1.4.0",
|
|
264008
|
+
operatorMethodCount: 329,
|
|
264009
264009
|
operatorEventCount: 31,
|
|
264010
264010
|
peerEndpointCount: 6
|
|
264011
264011
|
};
|
|
@@ -264019,7 +264019,7 @@ var init_operator_contract = __esm(() => {
|
|
|
264019
264019
|
product: {
|
|
264020
264020
|
id: "goodvibes",
|
|
264021
264021
|
surface: "operator",
|
|
264022
|
-
version: "1.
|
|
264022
|
+
version: "1.4.0"
|
|
264023
264023
|
},
|
|
264024
264024
|
auth: {
|
|
264025
264025
|
modes: [
|
|
@@ -286083,6 +286083,16 @@ var init_operator_contract = __esm(() => {
|
|
|
286083
286083
|
},
|
|
286084
286084
|
createdAt: {
|
|
286085
286085
|
type: "number"
|
|
286086
|
+
},
|
|
286087
|
+
deliveryState: {
|
|
286088
|
+
type: "string",
|
|
286089
|
+
enum: [
|
|
286090
|
+
"cancelled",
|
|
286091
|
+
"queued"
|
|
286092
|
+
]
|
|
286093
|
+
},
|
|
286094
|
+
inReplyTo: {
|
|
286095
|
+
type: "string"
|
|
286086
286096
|
}
|
|
286087
286097
|
},
|
|
286088
286098
|
required: [
|
|
@@ -286161,6 +286171,99 @@ var init_operator_contract = __esm(() => {
|
|
|
286161
286171
|
},
|
|
286162
286172
|
invokable: true
|
|
286163
286173
|
},
|
|
286174
|
+
{
|
|
286175
|
+
id: "companion.chat.messages.steer",
|
|
286176
|
+
title: "Steer Companion Chat (Interrupt And Send)",
|
|
286177
|
+
description: 'Send a message that runs IMMEDIATELY, interrupting the in-flight turn if one is running. The message jumps to the front of the pending-turn queue; the active turn is cancelled through the same finalization path as companion.chat.turns.cancel (any non-empty partial reply is persisted with `deliveryState: "cancelled"` and the terminal `turn.cancelled` event reaches every subscriber), then the steered message\'s turn starts. Messages queued behind an active turn keep their places behind the steer. With no turn running this behaves as an ordinary send. Accepts the same payload as companion.chat.messages.create (`body`/`content`, `attachments`, `metadata`). Returns the new message id, `steered: true`, and `cancelledTurnId` when a turn was interrupted. Ordinary sends posted while a turn is running are QUEUED (transcript-visible immediately with `deliveryState: "queued"`, answered in order) \u2014 steer is the explicit jump-the-line verb.',
|
|
286178
|
+
category: "companion",
|
|
286179
|
+
source: "builtin",
|
|
286180
|
+
access: "authenticated",
|
|
286181
|
+
transport: [
|
|
286182
|
+
"http",
|
|
286183
|
+
"ws"
|
|
286184
|
+
],
|
|
286185
|
+
scopes: [
|
|
286186
|
+
"write:sessions"
|
|
286187
|
+
],
|
|
286188
|
+
http: {
|
|
286189
|
+
method: "POST",
|
|
286190
|
+
path: "/api/companion/chat/sessions/{sessionId}/messages/steer"
|
|
286191
|
+
},
|
|
286192
|
+
inputSchema: {
|
|
286193
|
+
type: "object",
|
|
286194
|
+
properties: {
|
|
286195
|
+
sessionId: {
|
|
286196
|
+
type: "string"
|
|
286197
|
+
},
|
|
286198
|
+
body: {
|
|
286199
|
+
type: "string"
|
|
286200
|
+
},
|
|
286201
|
+
content: {
|
|
286202
|
+
type: "string"
|
|
286203
|
+
},
|
|
286204
|
+
attachments: {
|
|
286205
|
+
type: "array",
|
|
286206
|
+
items: {
|
|
286207
|
+
type: "object",
|
|
286208
|
+
properties: {
|
|
286209
|
+
artifactId: {
|
|
286210
|
+
type: "string"
|
|
286211
|
+
},
|
|
286212
|
+
label: {
|
|
286213
|
+
type: "string"
|
|
286214
|
+
},
|
|
286215
|
+
metadata: {
|
|
286216
|
+
type: "object",
|
|
286217
|
+
properties: {},
|
|
286218
|
+
additionalProperties: false
|
|
286219
|
+
}
|
|
286220
|
+
},
|
|
286221
|
+
required: [
|
|
286222
|
+
"artifactId"
|
|
286223
|
+
],
|
|
286224
|
+
additionalProperties: false
|
|
286225
|
+
}
|
|
286226
|
+
},
|
|
286227
|
+
metadata: {
|
|
286228
|
+
type: "object",
|
|
286229
|
+
properties: {},
|
|
286230
|
+
additionalProperties: false
|
|
286231
|
+
}
|
|
286232
|
+
},
|
|
286233
|
+
required: [
|
|
286234
|
+
"sessionId"
|
|
286235
|
+
],
|
|
286236
|
+
additionalProperties: true
|
|
286237
|
+
},
|
|
286238
|
+
outputSchema: {
|
|
286239
|
+
type: "object",
|
|
286240
|
+
properties: {
|
|
286241
|
+
sessionId: {
|
|
286242
|
+
type: "string"
|
|
286243
|
+
},
|
|
286244
|
+
messageId: {
|
|
286245
|
+
type: "string"
|
|
286246
|
+
},
|
|
286247
|
+
steered: {
|
|
286248
|
+
type: "boolean"
|
|
286249
|
+
},
|
|
286250
|
+
cancelledTurnId: {
|
|
286251
|
+
type: "string"
|
|
286252
|
+
},
|
|
286253
|
+
turnStarted: {
|
|
286254
|
+
type: "boolean"
|
|
286255
|
+
}
|
|
286256
|
+
},
|
|
286257
|
+
required: [
|
|
286258
|
+
"sessionId",
|
|
286259
|
+
"messageId",
|
|
286260
|
+
"steered",
|
|
286261
|
+
"turnStarted"
|
|
286262
|
+
],
|
|
286263
|
+
additionalProperties: false
|
|
286264
|
+
},
|
|
286265
|
+
invokable: true
|
|
286266
|
+
},
|
|
286164
286267
|
{
|
|
286165
286268
|
id: "companion.chat.sessions.close",
|
|
286166
286269
|
title: "Close Companion Chat Session",
|
|
@@ -286627,6 +286730,16 @@ var init_operator_contract = __esm(() => {
|
|
|
286627
286730
|
},
|
|
286628
286731
|
createdAt: {
|
|
286629
286732
|
type: "number"
|
|
286733
|
+
},
|
|
286734
|
+
deliveryState: {
|
|
286735
|
+
type: "string",
|
|
286736
|
+
enum: [
|
|
286737
|
+
"cancelled",
|
|
286738
|
+
"queued"
|
|
286739
|
+
]
|
|
286740
|
+
},
|
|
286741
|
+
inReplyTo: {
|
|
286742
|
+
type: "string"
|
|
286630
286743
|
}
|
|
286631
286744
|
},
|
|
286632
286745
|
required: [
|
|
@@ -286935,6 +287048,68 @@ var init_operator_contract = __esm(() => {
|
|
|
286935
287048
|
},
|
|
286936
287049
|
invokable: true
|
|
286937
287050
|
},
|
|
287051
|
+
{
|
|
287052
|
+
id: "companion.chat.turns.cancel",
|
|
287053
|
+
title: "Cancel Companion Chat Turn",
|
|
287054
|
+
description: 'Stop the in-flight turn for a companion chat session \u2014 a true server-side stop: the provider stream is aborted, any non-empty partial reply is persisted to the transcript with an explicit `deliveryState: "cancelled"` marker (an honest partial, never disguised as a complete reply) AND committed to the model-facing conversation history with an explicit interruption note \u2014 later turns can reason about what the user saw and stopped, which is usually what a follow-up or steer refers to, and the terminal `turn.cancelled` event is published to every subscriber of the session stream so a stop from one client converges on all others. Any announced tool call without a result is closed with a synthetic error `turn.tool_result` before the terminal event. Optional `turnId` guards against cancelling a newer turn a stale stop raced against (409 TURN_MISMATCH). No turn in flight is the benign 404 NO_ACTIVE_TURN (the turn finished before the stop landed). Repeat cancels are idempotent successes. The session stays open; the next message starts a fresh turn normally.',
|
|
287055
|
+
category: "companion",
|
|
287056
|
+
source: "builtin",
|
|
287057
|
+
access: "authenticated",
|
|
287058
|
+
transport: [
|
|
287059
|
+
"http",
|
|
287060
|
+
"ws"
|
|
287061
|
+
],
|
|
287062
|
+
scopes: [
|
|
287063
|
+
"write:sessions"
|
|
287064
|
+
],
|
|
287065
|
+
http: {
|
|
287066
|
+
method: "POST",
|
|
287067
|
+
path: "/api/companion/chat/sessions/{sessionId}/turns/cancel"
|
|
287068
|
+
},
|
|
287069
|
+
inputSchema: {
|
|
287070
|
+
type: "object",
|
|
287071
|
+
properties: {
|
|
287072
|
+
sessionId: {
|
|
287073
|
+
type: "string"
|
|
287074
|
+
},
|
|
287075
|
+
turnId: {
|
|
287076
|
+
type: "string"
|
|
287077
|
+
}
|
|
287078
|
+
},
|
|
287079
|
+
required: [
|
|
287080
|
+
"sessionId"
|
|
287081
|
+
],
|
|
287082
|
+
additionalProperties: true
|
|
287083
|
+
},
|
|
287084
|
+
outputSchema: {
|
|
287085
|
+
type: "object",
|
|
287086
|
+
properties: {
|
|
287087
|
+
sessionId: {
|
|
287088
|
+
type: "string"
|
|
287089
|
+
},
|
|
287090
|
+
turnId: {
|
|
287091
|
+
type: "string"
|
|
287092
|
+
},
|
|
287093
|
+
cancelled: {
|
|
287094
|
+
type: "boolean"
|
|
287095
|
+
},
|
|
287096
|
+
alreadyCancelled: {
|
|
287097
|
+
type: "boolean"
|
|
287098
|
+
},
|
|
287099
|
+
partialPersisted: {
|
|
287100
|
+
type: "boolean"
|
|
287101
|
+
}
|
|
287102
|
+
},
|
|
287103
|
+
required: [
|
|
287104
|
+
"sessionId",
|
|
287105
|
+
"turnId",
|
|
287106
|
+
"cancelled",
|
|
287107
|
+
"partialPersisted"
|
|
287108
|
+
],
|
|
287109
|
+
additionalProperties: false
|
|
287110
|
+
},
|
|
287111
|
+
invokable: true
|
|
287112
|
+
},
|
|
286938
287113
|
{
|
|
286939
287114
|
id: "config.get",
|
|
286940
287115
|
title: "Get Config",
|
|
@@ -345393,10 +345568,10 @@ var init_operator_contract = __esm(() => {
|
|
|
345393
345568
|
}
|
|
345394
345569
|
],
|
|
345395
345570
|
schemaCoverage: {
|
|
345396
|
-
methods:
|
|
345397
|
-
typedInputs:
|
|
345571
|
+
methods: 329,
|
|
345572
|
+
typedInputs: 329,
|
|
345398
345573
|
genericInputs: 0,
|
|
345399
|
-
typedOutputs:
|
|
345574
|
+
typedOutputs: 329,
|
|
345400
345575
|
genericOutputs: 0
|
|
345401
345576
|
},
|
|
345402
345577
|
eventCoverage: {
|
|
@@ -345405,8 +345580,8 @@ var init_operator_contract = __esm(() => {
|
|
|
345405
345580
|
withWireEvents: 31
|
|
345406
345581
|
},
|
|
345407
345582
|
validationCoverage: {
|
|
345408
|
-
methods:
|
|
345409
|
-
validated:
|
|
345583
|
+
methods: 329,
|
|
345584
|
+
validated: 327,
|
|
345410
345585
|
skippedGeneric: 0,
|
|
345411
345586
|
skippedUntyped: 2
|
|
345412
345587
|
}
|
|
@@ -345502,12 +345677,14 @@ var init_operator_method_ids = __esm(() => {
|
|
|
345502
345677
|
"companion.chat.messages.edit",
|
|
345503
345678
|
"companion.chat.messages.list",
|
|
345504
345679
|
"companion.chat.messages.retry",
|
|
345680
|
+
"companion.chat.messages.steer",
|
|
345505
345681
|
"companion.chat.sessions.close",
|
|
345506
345682
|
"companion.chat.sessions.create",
|
|
345507
345683
|
"companion.chat.sessions.delete",
|
|
345508
345684
|
"companion.chat.sessions.get",
|
|
345509
345685
|
"companion.chat.sessions.list",
|
|
345510
345686
|
"companion.chat.sessions.update",
|
|
345687
|
+
"companion.chat.turns.cancel",
|
|
345511
345688
|
"config.get",
|
|
345512
345689
|
"config.set",
|
|
345513
345690
|
"continuity.snapshot",
|
|
@@ -450511,7 +450688,9 @@ var init_operator_contract_schemas_runtime = __esm(() => {
|
|
|
450511
450688
|
role: COMPANION_CHAT_MESSAGE_ROLE_SCHEMA,
|
|
450512
450689
|
content: STRING_SCHEMA,
|
|
450513
450690
|
attachments: arraySchema(COMPANION_CHAT_ATTACHMENT_SCHEMA),
|
|
450514
|
-
createdAt: NUMBER_SCHEMA
|
|
450691
|
+
createdAt: NUMBER_SCHEMA,
|
|
450692
|
+
deliveryState: enumSchema(["cancelled", "queued"]),
|
|
450693
|
+
inReplyTo: STRING_SCHEMA
|
|
450515
450694
|
}, ["id", "sessionId", "role", "content", "attachments", "createdAt"]);
|
|
450516
450695
|
COMPANION_CHAT_SESSION_WITH_MESSAGES_SCHEMA = objectSchema({
|
|
450517
450696
|
session: COMPANION_CHAT_SESSION_SCHEMA,
|
|
@@ -453393,6 +453572,51 @@ var init_method_catalog_control_companion = __esm(() => {
|
|
|
453393
453572
|
turnStarted: BOOLEAN_SCHEMA
|
|
453394
453573
|
}, ["sessionId", "editedFrom", "messageId", "supersededMessageIds", "turnStarted"])
|
|
453395
453574
|
}),
|
|
453575
|
+
methodDescriptor({
|
|
453576
|
+
id: "companion.chat.messages.steer",
|
|
453577
|
+
title: "Steer Companion Chat (Interrupt And Send)",
|
|
453578
|
+
description: 'Send a message that runs IMMEDIATELY, interrupting the in-flight turn if one is running. The message jumps to the front of the pending-turn queue; the active turn is cancelled through the same finalization path as companion.chat.turns.cancel (any non-empty partial reply is persisted with `deliveryState: "cancelled"` and the terminal `turn.cancelled` event reaches every subscriber), then the steered message\'s turn starts. Messages queued behind an active turn keep their places behind the steer. With no turn running this behaves as an ordinary send. Accepts the same payload as companion.chat.messages.create (`body`/`content`, `attachments`, `metadata`). Returns the new message id, `steered: true`, and `cancelledTurnId` when a turn was interrupted. Ordinary sends posted while a turn is running are QUEUED (transcript-visible immediately with `deliveryState: "queued"`, answered in order) \u2014 steer is the explicit jump-the-line verb.',
|
|
453579
|
+
category: "companion",
|
|
453580
|
+
scopes: ["write:sessions"],
|
|
453581
|
+
http: { method: "POST", path: "/api/companion/chat/sessions/{sessionId}/messages/steer" },
|
|
453582
|
+
inputSchema: bodyEnvelopeSchema({
|
|
453583
|
+
sessionId: STRING_SCHEMA,
|
|
453584
|
+
body: STRING_SCHEMA,
|
|
453585
|
+
content: STRING_SCHEMA,
|
|
453586
|
+
attachments: arraySchema(objectSchema({
|
|
453587
|
+
artifactId: STRING_SCHEMA,
|
|
453588
|
+
label: STRING_SCHEMA,
|
|
453589
|
+
metadata: objectSchema({}, [])
|
|
453590
|
+
}, ["artifactId"])),
|
|
453591
|
+
metadata: objectSchema({}, [])
|
|
453592
|
+
}, ["sessionId"]),
|
|
453593
|
+
outputSchema: objectSchema({
|
|
453594
|
+
sessionId: STRING_SCHEMA,
|
|
453595
|
+
messageId: STRING_SCHEMA,
|
|
453596
|
+
steered: BOOLEAN_SCHEMA,
|
|
453597
|
+
cancelledTurnId: STRING_SCHEMA,
|
|
453598
|
+
turnStarted: BOOLEAN_SCHEMA
|
|
453599
|
+
}, ["sessionId", "messageId", "steered", "turnStarted"])
|
|
453600
|
+
}),
|
|
453601
|
+
methodDescriptor({
|
|
453602
|
+
id: "companion.chat.turns.cancel",
|
|
453603
|
+
title: "Cancel Companion Chat Turn",
|
|
453604
|
+
description: 'Stop the in-flight turn for a companion chat session \u2014 a true server-side stop: the provider stream is aborted, any non-empty partial reply is persisted to the transcript with an explicit `deliveryState: "cancelled"` marker (an honest partial, never disguised as a complete reply) AND committed to the model-facing conversation history with an explicit interruption note \u2014 later turns can reason about what the user saw and stopped, which is usually what a follow-up or steer refers to, and the terminal `turn.cancelled` event is published to every subscriber of the session stream so a stop from one client converges on all others. Any announced tool call without a result is closed with a synthetic error `turn.tool_result` before the terminal event. Optional `turnId` guards against cancelling a newer turn a stale stop raced against (409 TURN_MISMATCH). No turn in flight is the benign 404 NO_ACTIVE_TURN (the turn finished before the stop landed). Repeat cancels are idempotent successes. The session stays open; the next message starts a fresh turn normally.',
|
|
453605
|
+
category: "companion",
|
|
453606
|
+
scopes: ["write:sessions"],
|
|
453607
|
+
http: { method: "POST", path: "/api/companion/chat/sessions/{sessionId}/turns/cancel" },
|
|
453608
|
+
inputSchema: bodyEnvelopeSchema({
|
|
453609
|
+
sessionId: STRING_SCHEMA,
|
|
453610
|
+
turnId: STRING_SCHEMA
|
|
453611
|
+
}, ["sessionId"]),
|
|
453612
|
+
outputSchema: objectSchema({
|
|
453613
|
+
sessionId: STRING_SCHEMA,
|
|
453614
|
+
turnId: STRING_SCHEMA,
|
|
453615
|
+
cancelled: BOOLEAN_SCHEMA,
|
|
453616
|
+
alreadyCancelled: BOOLEAN_SCHEMA,
|
|
453617
|
+
partialPersisted: BOOLEAN_SCHEMA
|
|
453618
|
+
}, ["sessionId", "turnId", "cancelled", "partialPersisted"])
|
|
453619
|
+
}),
|
|
453396
453620
|
methodDescriptor({
|
|
453397
453621
|
id: "companion.chat.events.stream",
|
|
453398
453622
|
title: "Stream Companion Chat Events",
|
|
@@ -813219,6 +813443,12 @@ async function dispatchCompanionChatRoutes(req, context) {
|
|
|
813219
813443
|
if (sub === "messages/edit" && req.method === "POST") {
|
|
813220
813444
|
return handleEditMessage(req, sessionId, context);
|
|
813221
813445
|
}
|
|
813446
|
+
if (sub === "turns/cancel" && req.method === "POST") {
|
|
813447
|
+
return handleCancelTurn(req, sessionId, context);
|
|
813448
|
+
}
|
|
813449
|
+
if (sub === "messages/steer" && req.method === "POST") {
|
|
813450
|
+
return handleSteerMessage(req, sessionId, context);
|
|
813451
|
+
}
|
|
813222
813452
|
if (sub === "events" && req.method === "GET") {
|
|
813223
813453
|
return handleGetEvents(req, sessionId, context);
|
|
813224
813454
|
}
|
|
@@ -813475,6 +813705,42 @@ async function handleGetMessages(sessionId, context) {
|
|
|
813475
813705
|
const messages = context.chatManager.getMessages(sessionId);
|
|
813476
813706
|
return Response.json({ sessionId, messages });
|
|
813477
813707
|
}
|
|
813708
|
+
async function handleCancelTurn(req, sessionId, context) {
|
|
813709
|
+
const bodyOrResponse = await context.parseOptionalJsonBody(req);
|
|
813710
|
+
if (bodyOrResponse instanceof Response)
|
|
813711
|
+
return bodyOrResponse;
|
|
813712
|
+
const body2 = bodyOrResponse ?? {};
|
|
813713
|
+
const input = {
|
|
813714
|
+
turnId: typeof body2["turnId"] === "string" && body2["turnId"].trim() ? body2["turnId"].trim() : undefined
|
|
813715
|
+
};
|
|
813716
|
+
try {
|
|
813717
|
+
return Response.json(await context.chatManager.cancelTurn(sessionId, input));
|
|
813718
|
+
} catch (err2) {
|
|
813719
|
+
return respondWithManagerError(err2);
|
|
813720
|
+
}
|
|
813721
|
+
}
|
|
813722
|
+
async function handleSteerMessage(req, sessionId, context) {
|
|
813723
|
+
const bodyOrResponse = await context.parseJsonBody(req);
|
|
813724
|
+
if (bodyOrResponse instanceof Response)
|
|
813725
|
+
return bodyOrResponse;
|
|
813726
|
+
const body2 = bodyOrResponse;
|
|
813727
|
+
const rawContent = readCompanionChatMessageBody(body2);
|
|
813728
|
+
const attachments = readCompanionChatAttachments(body2);
|
|
813729
|
+
if (attachments instanceof Response)
|
|
813730
|
+
return attachments;
|
|
813731
|
+
if (!rawContent.trim() && attachments.length === 0) {
|
|
813732
|
+
return Response.json({ error: "content, body, or attachments are required", code: "INVALID_INPUT" }, { status: 400 });
|
|
813733
|
+
}
|
|
813734
|
+
try {
|
|
813735
|
+
const result = await context.chatManager.steerMessage(sessionId, rawContent, "", {
|
|
813736
|
+
attachments,
|
|
813737
|
+
metadata: typeof body2["metadata"] === "object" && body2["metadata"] !== null ? body2["metadata"] : undefined
|
|
813738
|
+
});
|
|
813739
|
+
return Response.json(result, { status: 202 });
|
|
813740
|
+
} catch (err2) {
|
|
813741
|
+
return respondWithManagerError(err2);
|
|
813742
|
+
}
|
|
813743
|
+
}
|
|
813478
813744
|
async function handleGetEvents(req, sessionId, context) {
|
|
813479
813745
|
const session2 = context.chatManager.getSession(sessionId);
|
|
813480
813746
|
if (!session2) {
|
|
@@ -814677,7 +814943,7 @@ class HomeAssistantConversationRoutes {
|
|
|
814677
814943
|
const body2 = await this.context.parseJsonBody(req);
|
|
814678
814944
|
if (body2 instanceof Response)
|
|
814679
814945
|
return body2;
|
|
814680
|
-
return this.cancelConversation(body2);
|
|
814946
|
+
return await this.cancelConversation(body2);
|
|
814681
814947
|
}
|
|
814682
814948
|
return null;
|
|
814683
814949
|
}
|
|
@@ -814863,14 +815129,24 @@ data: ${JSON.stringify(data)}
|
|
|
814863
815129
|
}
|
|
814864
815130
|
});
|
|
814865
815131
|
}
|
|
814866
|
-
cancelConversation(body2) {
|
|
815132
|
+
async cancelConversation(body2) {
|
|
814867
815133
|
const indexed = readString27(body2.messageId ?? body2.message_id) ? this.messageIndex.get(readString27(body2.messageId ?? body2.message_id)) : undefined;
|
|
814868
815134
|
const sessionId = readString27(body2.sessionId ?? body2.session_id) ?? indexed?.sessionId;
|
|
814869
815135
|
if (!sessionId) {
|
|
814870
815136
|
return Response.json({ ok: false, error: "sessionId or known messageId is required." }, { status: 400 });
|
|
814871
815137
|
}
|
|
814872
|
-
|
|
814873
|
-
|
|
815138
|
+
if (!this.context.chatManager.getSession(sessionId)) {
|
|
815139
|
+
return Response.json({ ok: false, sessionId, error: "Unknown Home Assistant chat session." }, { status: 404 });
|
|
815140
|
+
}
|
|
815141
|
+
try {
|
|
815142
|
+
await this.context.chatManager.cancelTurn(sessionId);
|
|
815143
|
+
} catch (error51) {
|
|
815144
|
+
const code2 = error51.code;
|
|
815145
|
+
if (code2 !== "NO_ACTIVE_TURN") {
|
|
815146
|
+
return Response.json({ ok: false, sessionId, error: error51 instanceof Error ? error51.message : String(error51) }, { status: 500 });
|
|
815147
|
+
}
|
|
815148
|
+
}
|
|
815149
|
+
return Response.json({ ok: true, sessionId, status: "cancelled" });
|
|
814874
815150
|
}
|
|
814875
815151
|
parseInput(body2) {
|
|
814876
815152
|
const threadId = readString27(body2.threadId ?? body2.thread_id);
|
|
@@ -817015,6 +817291,135 @@ var init_companion_chat_rate_limiter = __esm(() => {
|
|
|
817015
817291
|
init_dist();
|
|
817016
817292
|
});
|
|
817017
817293
|
|
|
817294
|
+
// node_modules/@pellux/goodvibes-sdk/dist/platform/companion/companion-chat-turn-control.js
|
|
817295
|
+
function createTurnAbortScope(turnId, sessionSignal) {
|
|
817296
|
+
const controller = new AbortController;
|
|
817297
|
+
const onSessionAbort = () => {
|
|
817298
|
+
controller.abort();
|
|
817299
|
+
};
|
|
817300
|
+
if (sessionSignal.aborted)
|
|
817301
|
+
controller.abort();
|
|
817302
|
+
else
|
|
817303
|
+
sessionSignal.addEventListener("abort", onSessionAbort, { once: true });
|
|
817304
|
+
let settle;
|
|
817305
|
+
const settled = new Promise((resolve31) => {
|
|
817306
|
+
settle = resolve31;
|
|
817307
|
+
});
|
|
817308
|
+
const activeTurn = { turnId, controller, cancelRequested: false, settled };
|
|
817309
|
+
return {
|
|
817310
|
+
activeTurn,
|
|
817311
|
+
abortSignal: controller.signal,
|
|
817312
|
+
settle,
|
|
817313
|
+
detach: () => {
|
|
817314
|
+
sessionSignal.removeEventListener("abort", onSessionAbort);
|
|
817315
|
+
}
|
|
817316
|
+
};
|
|
817317
|
+
}
|
|
817318
|
+
function finalizeCancelledTurn(ctx) {
|
|
817319
|
+
const { sessionId, turnId } = ctx;
|
|
817320
|
+
const stoppedBy = ctx.wasCancelRequested() ? "user" : ctx.isShutdown() ? "shutdown" : "session-closed";
|
|
817321
|
+
for (const [toolCallId, toolName] of ctx.openToolCalls) {
|
|
817322
|
+
ctx.publish({
|
|
817323
|
+
type: "turn.tool_result",
|
|
817324
|
+
sessionId,
|
|
817325
|
+
turnId,
|
|
817326
|
+
toolCallId,
|
|
817327
|
+
toolName,
|
|
817328
|
+
result: "Cancelled: the turn was stopped before this tool call completed.",
|
|
817329
|
+
isError: true
|
|
817330
|
+
});
|
|
817331
|
+
}
|
|
817332
|
+
ctx.openToolCalls.clear();
|
|
817333
|
+
const assistantContent = ctx.getAssistantContent();
|
|
817334
|
+
const uncommitted = ctx.getUncommittedContent();
|
|
817335
|
+
if (uncommitted.trim()) {
|
|
817336
|
+
ctx.commitPartialToHistory(uncommitted + TURN_INTERRUPTION_NOTE);
|
|
817337
|
+
}
|
|
817338
|
+
let persistedId;
|
|
817339
|
+
let envelope;
|
|
817340
|
+
if (assistantContent.trim()) {
|
|
817341
|
+
const now4 = Date.now();
|
|
817342
|
+
ctx.persistPartial({
|
|
817343
|
+
id: ctx.assistantMessageId,
|
|
817344
|
+
sessionId,
|
|
817345
|
+
role: "assistant",
|
|
817346
|
+
content: assistantContent,
|
|
817347
|
+
attachments: [],
|
|
817348
|
+
createdAt: now4,
|
|
817349
|
+
deliveryState: "cancelled",
|
|
817350
|
+
inReplyTo: ctx.userMessageId
|
|
817351
|
+
});
|
|
817352
|
+
persistedId = ctx.assistantMessageId;
|
|
817353
|
+
envelope = {
|
|
817354
|
+
sessionId,
|
|
817355
|
+
messageId: ctx.assistantMessageId,
|
|
817356
|
+
body: assistantContent,
|
|
817357
|
+
source: "companion-chat-assistant",
|
|
817358
|
+
timestamp: now4
|
|
817359
|
+
};
|
|
817360
|
+
}
|
|
817361
|
+
ctx.publish({
|
|
817362
|
+
type: "turn.cancelled",
|
|
817363
|
+
sessionId,
|
|
817364
|
+
turnId,
|
|
817365
|
+
stoppedBy,
|
|
817366
|
+
partialPersisted: persistedId !== undefined,
|
|
817367
|
+
...persistedId !== undefined ? { assistantMessageId: persistedId, envelope } : {}
|
|
817368
|
+
});
|
|
817369
|
+
ctx.resolveReply(persistedId !== undefined ? { assistantMessageId: persistedId, response: assistantContent } : {});
|
|
817370
|
+
ctx.settle({
|
|
817371
|
+
partialPersisted: persistedId !== undefined,
|
|
817372
|
+
...persistedId !== undefined ? { assistantMessageId: persistedId } : {}
|
|
817373
|
+
});
|
|
817374
|
+
}
|
|
817375
|
+
async function cancelActiveTurn(sessionId, turn2, input) {
|
|
817376
|
+
if (!turn2) {
|
|
817377
|
+
throw Object.assign(new Error("No turn is in flight for this session \u2014 it may have finished before the stop landed."), { code: "NO_ACTIVE_TURN", status: 404 });
|
|
817378
|
+
}
|
|
817379
|
+
if (input.turnId !== undefined && input.turnId !== turn2.turnId) {
|
|
817380
|
+
throw Object.assign(new Error("The requested turn is not the active turn \u2014 a newer turn is already running."), { code: "TURN_MISMATCH", status: 409 });
|
|
817381
|
+
}
|
|
817382
|
+
const alreadyCancelled = turn2.cancelRequested;
|
|
817383
|
+
if (!alreadyCancelled) {
|
|
817384
|
+
turn2.cancelRequested = true;
|
|
817385
|
+
turn2.controller.abort();
|
|
817386
|
+
}
|
|
817387
|
+
const settled = await Promise.race([
|
|
817388
|
+
turn2.settled,
|
|
817389
|
+
new Promise((resolve31) => setTimeout(resolve31, CANCEL_SETTLE_TIMEOUT_MS))
|
|
817390
|
+
]);
|
|
817391
|
+
return {
|
|
817392
|
+
sessionId,
|
|
817393
|
+
turnId: turn2.turnId,
|
|
817394
|
+
cancelled: true,
|
|
817395
|
+
...alreadyCancelled ? { alreadyCancelled: true } : {},
|
|
817396
|
+
partialPersisted: settled?.partialPersisted ?? false
|
|
817397
|
+
};
|
|
817398
|
+
}
|
|
817399
|
+
function awaitCompanionReply(timeoutMs, post, onTimeout) {
|
|
817400
|
+
let messageId = "";
|
|
817401
|
+
return new Promise((resolve31) => {
|
|
817402
|
+
const timeout = setTimeout(() => {
|
|
817403
|
+
if (messageId)
|
|
817404
|
+
onTimeout(messageId);
|
|
817405
|
+
resolve31({ messageId, error: "Timed out waiting for companion chat reply" });
|
|
817406
|
+
}, timeoutMs);
|
|
817407
|
+
timeout.unref?.();
|
|
817408
|
+
post({ resolve: resolve31, timeout }).then((id) => {
|
|
817409
|
+
messageId = id;
|
|
817410
|
+
}).catch((error51) => {
|
|
817411
|
+
clearTimeout(timeout);
|
|
817412
|
+
resolve31({
|
|
817413
|
+
messageId,
|
|
817414
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
817415
|
+
});
|
|
817416
|
+
});
|
|
817417
|
+
});
|
|
817418
|
+
}
|
|
817419
|
+
var TURN_INTERRUPTION_NOTE = `
|
|
817420
|
+
|
|
817421
|
+
[Interrupted: the user stopped this response here, before it was complete.]`, CANCEL_SETTLE_TIMEOUT_MS = 3000;
|
|
817422
|
+
|
|
817018
817423
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/companion/companion-chat-manager.js
|
|
817019
817424
|
import { randomUUID as randomUUID43 } from "crypto";
|
|
817020
817425
|
function assertCompleteProviderModelRoute(input) {
|
|
@@ -817045,6 +817450,7 @@ class CompanionChatManager {
|
|
|
817045
817450
|
gcTimer = null;
|
|
817046
817451
|
initCompleted = false;
|
|
817047
817452
|
pendingReplies = new Map;
|
|
817453
|
+
disposed = false;
|
|
817048
817454
|
_pendingSaves = new Map;
|
|
817049
817455
|
constructor(config6) {
|
|
817050
817456
|
this.provider = config6.provider;
|
|
@@ -817198,6 +817604,13 @@ class CompanionChatManager {
|
|
|
817198
817604
|
this._brokerSync.track(sessionId, () => this._brokerSync.closeSession(sessionId));
|
|
817199
817605
|
return updated;
|
|
817200
817606
|
}
|
|
817607
|
+
async cancelTurn(sessionId, input = {}) {
|
|
817608
|
+
const session2 = this.sessions.get(sessionId);
|
|
817609
|
+
if (!session2) {
|
|
817610
|
+
throw Object.assign(new Error(`Session not found: ${sessionId}`), { code: "SESSION_NOT_FOUND", status: 404 });
|
|
817611
|
+
}
|
|
817612
|
+
return cancelActiveTurn(sessionId, session2.activeTurn ?? null, input);
|
|
817613
|
+
}
|
|
817201
817614
|
async deleteSession(sessionId) {
|
|
817202
817615
|
const session2 = this.sessions.get(sessionId);
|
|
817203
817616
|
if (!session2) {
|
|
@@ -817218,29 +817631,11 @@ class CompanionChatManager {
|
|
|
817218
817631
|
});
|
|
817219
817632
|
}
|
|
817220
817633
|
async postMessageAndWaitForReply(sessionId, content, clientId = "", options = {}) {
|
|
817221
|
-
|
|
817222
|
-
|
|
817223
|
-
|
|
817224
|
-
|
|
817225
|
-
|
|
817226
|
-
resolve31({ messageId, error: "Timed out waiting for companion chat reply" });
|
|
817227
|
-
}, options.timeoutMs ?? 120000);
|
|
817228
|
-
timeout.unref?.();
|
|
817229
|
-
this._postMessageInternal(sessionId, content, clientId, {
|
|
817230
|
-
pendingReply: { resolve: resolve31, timeout },
|
|
817231
|
-
attachments: options.attachments,
|
|
817232
|
-
...options.onTurnEvent ? { onTurnEvent: options.onTurnEvent } : {}
|
|
817233
|
-
}).then((id) => {
|
|
817234
|
-
messageId = id;
|
|
817235
|
-
}).catch((error51) => {
|
|
817236
|
-
clearTimeout(timeout);
|
|
817237
|
-
resolve31({
|
|
817238
|
-
messageId,
|
|
817239
|
-
error: error51 instanceof Error ? error51.message : String(error51)
|
|
817240
|
-
});
|
|
817241
|
-
});
|
|
817242
|
-
});
|
|
817243
|
-
return result;
|
|
817634
|
+
return awaitCompanionReply(options.timeoutMs ?? 120000, (pendingReply) => this._postMessageInternal(sessionId, content, clientId, {
|
|
817635
|
+
pendingReply,
|
|
817636
|
+
attachments: options.attachments,
|
|
817637
|
+
...options.onTurnEvent ? { onTurnEvent: options.onTurnEvent } : {}
|
|
817638
|
+
}), (messageId) => this.pendingReplies.delete(messageId));
|
|
817244
817639
|
}
|
|
817245
817640
|
async _postMessageInternal(sessionId, content, clientId, options = {}) {
|
|
817246
817641
|
const session2 = this.sessions.get(sessionId);
|
|
@@ -817257,6 +817652,7 @@ class CompanionChatManager {
|
|
|
817257
817652
|
}
|
|
817258
817653
|
const messageId = randomUUID43();
|
|
817259
817654
|
const now4 = Date.now();
|
|
817655
|
+
const queuedBehindActiveTurn = session2.activeTurn != null && options.steer !== true;
|
|
817260
817656
|
const userMsg = {
|
|
817261
817657
|
id: messageId,
|
|
817262
817658
|
sessionId,
|
|
@@ -817264,10 +817660,11 @@ class CompanionChatManager {
|
|
|
817264
817660
|
content,
|
|
817265
817661
|
attachments,
|
|
817266
817662
|
...options.metadata !== undefined ? { metadata: options.metadata } : {},
|
|
817267
|
-
createdAt: now4
|
|
817663
|
+
createdAt: now4,
|
|
817664
|
+
...queuedBehindActiveTurn ? { deliveryState: "queued" } : {}
|
|
817268
817665
|
};
|
|
817666
|
+
const providerContent = await buildProviderUserContent(content, attachments, this.artifactStore);
|
|
817269
817667
|
session2.messages.push(userMsg);
|
|
817270
|
-
session2.conversation.addUserMessage(await buildProviderUserContent(content, attachments, this.artifactStore));
|
|
817271
817668
|
session2.lastActivityAt = now4;
|
|
817272
817669
|
this._updateMeta(session2, {
|
|
817273
817670
|
messageCount: session2.messages.length,
|
|
@@ -817277,16 +817674,21 @@ class CompanionChatManager {
|
|
|
817277
817674
|
if (options.pendingReply) {
|
|
817278
817675
|
this.pendingReplies.set(messageId, options.pendingReply);
|
|
817279
817676
|
}
|
|
817280
|
-
|
|
817281
|
-
|
|
817282
|
-
|
|
817283
|
-
|
|
817284
|
-
|
|
817285
|
-
|
|
817286
|
-
|
|
817677
|
+
const entry = {
|
|
817678
|
+
userMessageId: messageId,
|
|
817679
|
+
providerContent,
|
|
817680
|
+
...options.onTurnEvent ? { onTurnEvent: options.onTurnEvent } : {}
|
|
817681
|
+
};
|
|
817682
|
+
const queue = session2.pendingTurns ??= [];
|
|
817683
|
+
if (options.steer === true)
|
|
817684
|
+
queue.unshift(entry);
|
|
817685
|
+
else
|
|
817686
|
+
queue.push(entry);
|
|
817687
|
+
this._startNextTurn(session2);
|
|
817287
817688
|
return messageId;
|
|
817288
817689
|
}
|
|
817289
817690
|
dispose() {
|
|
817691
|
+
this.disposed = true;
|
|
817290
817692
|
if (this.gcTimer) {
|
|
817291
817693
|
clearInterval(this.gcTimer);
|
|
817292
817694
|
this.gcTimer = null;
|
|
@@ -817296,11 +817698,70 @@ class CompanionChatManager {
|
|
|
817296
817698
|
}
|
|
817297
817699
|
this.sessions.clear();
|
|
817298
817700
|
}
|
|
817701
|
+
async steerMessage(sessionId, content, clientId = "", options = {}) {
|
|
817702
|
+
const activeBefore = this.sessions.get(sessionId)?.activeTurn ?? null;
|
|
817703
|
+
const messageId = await this._postMessageInternal(sessionId, content, clientId, {
|
|
817704
|
+
attachments: options.attachments,
|
|
817705
|
+
metadata: options.metadata,
|
|
817706
|
+
steer: true
|
|
817707
|
+
});
|
|
817708
|
+
let cancelledTurnId;
|
|
817709
|
+
if (activeBefore) {
|
|
817710
|
+
try {
|
|
817711
|
+
const result = await this.cancelTurn(sessionId, { turnId: activeBefore.turnId });
|
|
817712
|
+
cancelledTurnId = result.turnId;
|
|
817713
|
+
} catch (err2) {
|
|
817714
|
+
const code2 = err2.code;
|
|
817715
|
+
if (code2 !== "NO_ACTIVE_TURN" && code2 !== "TURN_MISMATCH")
|
|
817716
|
+
throw err2;
|
|
817717
|
+
}
|
|
817718
|
+
}
|
|
817719
|
+
const session2 = this.sessions.get(sessionId);
|
|
817720
|
+
if (session2)
|
|
817721
|
+
this._startNextTurn(session2);
|
|
817722
|
+
return {
|
|
817723
|
+
sessionId,
|
|
817724
|
+
messageId,
|
|
817725
|
+
steered: true,
|
|
817726
|
+
...cancelledTurnId !== undefined ? { cancelledTurnId } : {},
|
|
817727
|
+
turnStarted: true
|
|
817728
|
+
};
|
|
817729
|
+
}
|
|
817730
|
+
_startNextTurn(session2) {
|
|
817731
|
+
if (session2.activeTurn || session2.meta.status === "closed")
|
|
817732
|
+
return;
|
|
817733
|
+
const next = session2.pendingTurns?.shift();
|
|
817734
|
+
if (!next)
|
|
817735
|
+
return;
|
|
817736
|
+
const idx = session2.messages.findIndex((m3) => m3.id === next.userMessageId);
|
|
817737
|
+
const queuedMsg = idx >= 0 ? session2.messages[idx] : undefined;
|
|
817738
|
+
if (queuedMsg?.deliveryState === "queued") {
|
|
817739
|
+
const { deliveryState: _cleared, ...delivered } = queuedMsg;
|
|
817740
|
+
session2.messages[idx] = delivered;
|
|
817741
|
+
this._persist(session2.meta.id);
|
|
817742
|
+
}
|
|
817743
|
+
session2.conversation.addUserMessage(next.providerContent);
|
|
817744
|
+
this._runTurn(session2, next.userMessageId, next.onTurnEvent).catch((error51) => {
|
|
817745
|
+
logger.warn("[companion-chat] turn execution failed", {
|
|
817746
|
+
sessionId: session2.meta.id,
|
|
817747
|
+
messageId: next.userMessageId,
|
|
817748
|
+
error: summarizeError(error51)
|
|
817749
|
+
});
|
|
817750
|
+
});
|
|
817751
|
+
}
|
|
817299
817752
|
async _runTurn(session2, userMessageId, onTurnEvent) {
|
|
817300
817753
|
const turnId = randomUUID43();
|
|
817301
817754
|
const sessionId = session2.meta.id;
|
|
817302
|
-
const
|
|
817755
|
+
const scope = createTurnAbortScope(turnId, session2.abortController.signal);
|
|
817756
|
+
const abortSignal = scope.abortSignal;
|
|
817757
|
+
session2.activeTurn = scope.activeTurn;
|
|
817758
|
+
const openToolCalls = new Map;
|
|
817303
817759
|
const publish = (event) => {
|
|
817760
|
+
if (event.type === "turn.tool_call" && event.toolCallId) {
|
|
817761
|
+
openToolCalls.set(event.toolCallId, event.toolName);
|
|
817762
|
+
} else if (event.type === "turn.tool_result" && event.toolCallId) {
|
|
817763
|
+
openToolCalls.delete(event.toolCallId);
|
|
817764
|
+
}
|
|
817304
817765
|
this.eventPublisher.publishEvent(`companion-chat.${event.type}`, event, session2.subscriberClientId ? { clientId: session2.subscriberClientId } : undefined);
|
|
817305
817766
|
try {
|
|
817306
817767
|
onTurnEvent?.(event);
|
|
@@ -817317,7 +817778,31 @@ class CompanionChatManager {
|
|
|
817317
817778
|
};
|
|
817318
817779
|
publish({ type: "turn.started", sessionId, messageId: userMessageId, turnId, envelope: startEnvelope });
|
|
817319
817780
|
let assistantContent = "";
|
|
817781
|
+
let uncommittedContent = "";
|
|
817320
817782
|
const assistantMessageId = randomUUID43();
|
|
817783
|
+
const finalizeCancelled = () => finalizeCancelledTurn({
|
|
817784
|
+
sessionId,
|
|
817785
|
+
turnId,
|
|
817786
|
+
assistantMessageId,
|
|
817787
|
+
userMessageId,
|
|
817788
|
+
getAssistantContent: () => assistantContent,
|
|
817789
|
+
getUncommittedContent: () => uncommittedContent,
|
|
817790
|
+
commitPartialToHistory: (content) => {
|
|
817791
|
+
session2.conversation.addAssistantMessage(content);
|
|
817792
|
+
},
|
|
817793
|
+
openToolCalls,
|
|
817794
|
+
wasCancelRequested: () => scope.activeTurn.cancelRequested,
|
|
817795
|
+
isShutdown: () => this.disposed,
|
|
817796
|
+
publish,
|
|
817797
|
+
persistPartial: (message) => {
|
|
817798
|
+
session2.messages.push(message);
|
|
817799
|
+
session2.lastActivityAt = message.createdAt;
|
|
817800
|
+
this._updateMeta(session2, { messageCount: session2.messages.length, updatedAt: message.createdAt });
|
|
817801
|
+
this._persist(sessionId);
|
|
817802
|
+
},
|
|
817803
|
+
resolveReply: (extra) => this.resolvePendingReply(userMessageId, { messageId: userMessageId, ...extra, error: "Turn cancelled" }),
|
|
817804
|
+
settle: scope.settle
|
|
817805
|
+
});
|
|
817321
817806
|
try {
|
|
817322
817807
|
const toolDefinitions = this.toolRegistry?.getToolDefinitions() ?? [];
|
|
817323
817808
|
let completed = false;
|
|
@@ -817329,7 +817814,7 @@ class CompanionChatManager {
|
|
|
817329
817814
|
tools: toolDefinitions,
|
|
817330
817815
|
abortSignal
|
|
817331
817816
|
});
|
|
817332
|
-
|
|
817817
|
+
uncommittedContent = "";
|
|
817333
817818
|
const toolCalls = [];
|
|
817334
817819
|
for await (const chunk of stream6) {
|
|
817335
817820
|
if (abortSignal.aborted)
|
|
@@ -817337,7 +817822,7 @@ class CompanionChatManager {
|
|
|
817337
817822
|
switch (chunk.type) {
|
|
817338
817823
|
case "text_delta": {
|
|
817339
817824
|
const delta = chunk.delta ?? "";
|
|
817340
|
-
|
|
817825
|
+
uncommittedContent += delta;
|
|
817341
817826
|
assistantContent += delta;
|
|
817342
817827
|
publish({ type: "turn.delta", sessionId, turnId, delta });
|
|
817343
817828
|
break;
|
|
@@ -817381,11 +817866,13 @@ class CompanionChatManager {
|
|
|
817381
817866
|
if (abortSignal.aborted)
|
|
817382
817867
|
break;
|
|
817383
817868
|
if (toolCalls.length === 0) {
|
|
817384
|
-
session2.conversation.addAssistantMessage(
|
|
817869
|
+
session2.conversation.addAssistantMessage(uncommittedContent);
|
|
817870
|
+
uncommittedContent = "";
|
|
817385
817871
|
completed = true;
|
|
817386
817872
|
break;
|
|
817387
817873
|
}
|
|
817388
|
-
session2.conversation.addAssistantMessage(
|
|
817874
|
+
session2.conversation.addAssistantMessage(uncommittedContent, { toolCalls });
|
|
817875
|
+
uncommittedContent = "";
|
|
817389
817876
|
if (!this.toolRegistry) {
|
|
817390
817877
|
completed = true;
|
|
817391
817878
|
break;
|
|
@@ -817407,7 +817894,7 @@ class CompanionChatManager {
|
|
|
817407
817894
|
}
|
|
817408
817895
|
}
|
|
817409
817896
|
if (abortSignal.aborted) {
|
|
817410
|
-
|
|
817897
|
+
finalizeCancelled();
|
|
817411
817898
|
return;
|
|
817412
817899
|
}
|
|
817413
817900
|
const now4 = Date.now();
|
|
@@ -817417,7 +817904,8 @@ class CompanionChatManager {
|
|
|
817417
817904
|
role: "assistant",
|
|
817418
817905
|
content: assistantContent,
|
|
817419
817906
|
attachments: [],
|
|
817420
|
-
createdAt: now4
|
|
817907
|
+
createdAt: now4,
|
|
817908
|
+
inReplyTo: userMessageId
|
|
817421
817909
|
};
|
|
817422
817910
|
session2.messages.push(assistantMsg);
|
|
817423
817911
|
session2.lastActivityAt = now4;
|
|
@@ -817438,8 +817926,14 @@ class CompanionChatManager {
|
|
|
817438
817926
|
publish({ type: "turn.error", sessionId, turnId, error: errorMessage });
|
|
817439
817927
|
this.resolvePendingReply(userMessageId, { messageId: userMessageId, error: errorMessage });
|
|
817440
817928
|
} else {
|
|
817441
|
-
|
|
817929
|
+
finalizeCancelled();
|
|
817442
817930
|
}
|
|
817931
|
+
} finally {
|
|
817932
|
+
if (session2.activeTurn === scope.activeTurn)
|
|
817933
|
+
session2.activeTurn = null;
|
|
817934
|
+
scope.detach();
|
|
817935
|
+
scope.settle({ partialPersisted: false });
|
|
817936
|
+
this._startNextTurn(session2);
|
|
817443
817937
|
}
|
|
817444
817938
|
}
|
|
817445
817939
|
regenerateMessage(sessionId, input = {}) {
|
|
@@ -837124,7 +837618,7 @@ var createStyledCell = (char, overrides = {}) => ({
|
|
|
837124
837618
|
// src/version.ts
|
|
837125
837619
|
import { readFileSync } from "fs";
|
|
837126
837620
|
import { join } from "path";
|
|
837127
|
-
var _version = "1.6.
|
|
837621
|
+
var _version = "1.6.3";
|
|
837128
837622
|
try {
|
|
837129
837623
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf-8"));
|
|
837130
837624
|
_version = typeof pkg.version === "string" ? pkg.version : _version;
|
|
@@ -869782,7 +870276,11 @@ function createBrowserKnowledgeSdkFromRoutes(knowledgeRoutes, options = {}) {
|
|
|
869782
870276
|
},
|
|
869783
870277
|
messages: {
|
|
869784
870278
|
create: (id, input) => invoke("companion.chat.messages.create", { sessionId: id, ...input }),
|
|
869785
|
-
list: (id) => invoke("companion.chat.messages.list", { sessionId: id })
|
|
870279
|
+
list: (id) => invoke("companion.chat.messages.list", { sessionId: id }),
|
|
870280
|
+
steer: (id, input) => invoke("companion.chat.messages.steer", { sessionId: id, ...input })
|
|
870281
|
+
},
|
|
870282
|
+
turns: {
|
|
870283
|
+
cancel: (id, input) => invoke("companion.chat.turns.cancel", { sessionId: id, ...input ?? {} })
|
|
869786
870284
|
},
|
|
869787
870285
|
events: {
|
|
869788
870286
|
stream: (id, handlers, options2) => sdk.streams.open("/api/companion/chat/sessions/" + id + "/events", handlers, options2)
|
|
@@ -894332,13 +894830,13 @@ init_state();
|
|
|
894332
894830
|
|
|
894333
894831
|
// src/input/commands/recall-shared.ts
|
|
894334
894832
|
var VALID_CLASSES = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
|
|
894335
|
-
var
|
|
894833
|
+
var VALID_SCOPES = ["session", "project", "team"];
|
|
894336
894834
|
var VALID_REVIEW_STATES = ["fresh", "reviewed", "stale", "contradicted"];
|
|
894337
894835
|
function isValidClass(s4) {
|
|
894338
894836
|
return VALID_CLASSES.includes(s4);
|
|
894339
894837
|
}
|
|
894340
894838
|
function isValidScope(s4) {
|
|
894341
|
-
return
|
|
894839
|
+
return VALID_SCOPES.includes(s4);
|
|
894342
894840
|
}
|
|
894343
894841
|
function isValidReviewState(s4) {
|
|
894344
894842
|
return VALID_REVIEW_STATES.includes(s4);
|
|
@@ -894381,7 +894879,7 @@ function handleRecallSearch(args2, context) {
|
|
|
894381
894879
|
if (isValidScope(scope))
|
|
894382
894880
|
filter.scope = scope;
|
|
894383
894881
|
else {
|
|
894384
|
-
context.print(`[memory] Unknown scope "${scope}". Valid values ${
|
|
894882
|
+
context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894385
894883
|
return;
|
|
894386
894884
|
}
|
|
894387
894885
|
}
|
|
@@ -894556,7 +895054,7 @@ function handleRecallList(args2, context) {
|
|
|
894556
895054
|
if (scopeIdx !== -1 && args2[scopeIdx + 1]) {
|
|
894557
895055
|
const scope = args2[scopeIdx + 1];
|
|
894558
895056
|
if (!isValidScope(scope)) {
|
|
894559
|
-
context.print(`[memory] Unknown scope "${scope}". Valid values ${
|
|
895057
|
+
context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894560
895058
|
return;
|
|
894561
895059
|
}
|
|
894562
895060
|
filter.scope = scope;
|
|
@@ -894606,7 +895104,7 @@ async function handleRecallAdd(args2, context) {
|
|
|
894606
895104
|
const tags = tagsRaw ? tagsRaw.split(",").map((token) => token.trim()).filter(Boolean) : [];
|
|
894607
895105
|
const scope = scopeRaw && isValidScope(scopeRaw) ? scopeRaw : "project";
|
|
894608
895106
|
if (scopeRaw && !isValidScope(scopeRaw)) {
|
|
894609
|
-
context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${
|
|
895107
|
+
context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894610
895108
|
return;
|
|
894611
895109
|
}
|
|
894612
895110
|
const provenance = [];
|
|
@@ -894741,7 +895239,7 @@ function handleRecallExport(args2, context) {
|
|
|
894741
895239
|
if (scopeIdx !== -1 && commandArgs[scopeIdx + 1]) {
|
|
894742
895240
|
const scope = commandArgs[scopeIdx + 1];
|
|
894743
895241
|
if (!isValidScope(scope)) {
|
|
894744
|
-
context.print(`[memory] Unknown scope "${scope}". Valid values ${
|
|
895242
|
+
context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894745
895243
|
return;
|
|
894746
895244
|
}
|
|
894747
895245
|
filter.scope = scope;
|
|
@@ -894820,7 +895318,7 @@ function handleRecallHandoffExport(args2, context) {
|
|
|
894820
895318
|
const scopeIdx = commandArgs.indexOf("--scope");
|
|
894821
895319
|
const scopeRaw = scopeIdx !== -1 ? commandArgs[scopeIdx + 1] : "team";
|
|
894822
895320
|
if (!scopeRaw || !isValidScope(scopeRaw)) {
|
|
894823
|
-
context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${
|
|
895321
|
+
context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894824
895322
|
return;
|
|
894825
895323
|
}
|
|
894826
895324
|
const bundle = memory.exportBundle({ scope: scopeRaw });
|
|
@@ -894936,7 +895434,7 @@ function handleRecallPromote(args2, context) {
|
|
|
894936
895434
|
const id = parsed.rest[0];
|
|
894937
895435
|
const scope = parsed.rest[1];
|
|
894938
895436
|
if (!id || !scope || !isValidScope(scope)) {
|
|
894939
|
-
context.print(`[memory] Usage: /memory promote <id> <${
|
|
895437
|
+
context.print(`[memory] Usage: /memory promote <id> <${VALID_SCOPES.join("|")}> --yes`);
|
|
894940
895438
|
return;
|
|
894941
895439
|
}
|
|
894942
895440
|
if (!parsed.yes) {
|
|
@@ -908526,7 +909024,7 @@ async function tryWireMemoryCommand(runtime2, normalized, rest) {
|
|
|
908526
909024
|
|
|
908527
909025
|
// src/cli/memory-command.ts
|
|
908528
909026
|
var VALID_CLASSES2 = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
|
|
908529
|
-
var
|
|
909027
|
+
var VALID_SCOPES3 = ["session", "project", "team"];
|
|
908530
909028
|
var VALID_REVIEW_STATES3 = ["fresh", "reviewed", "stale", "contradicted"];
|
|
908531
909029
|
var VALID_PROVENANCE_KINDS = ["session", "turn", "task", "event", "file"];
|
|
908532
909030
|
var VALUE_OPTIONS = new Set([
|
|
@@ -908626,7 +909124,7 @@ function isMemoryClass2(value) {
|
|
|
908626
909124
|
return VALID_CLASSES2.includes(value);
|
|
908627
909125
|
}
|
|
908628
909126
|
function isMemoryScope2(value) {
|
|
908629
|
-
return
|
|
909127
|
+
return VALID_SCOPES3.includes(value);
|
|
908630
909128
|
}
|
|
908631
909129
|
function isReviewState(value) {
|
|
908632
909130
|
return VALID_REVIEW_STATES3.includes(value);
|
|
@@ -908641,7 +909139,7 @@ function requireClass(value) {
|
|
|
908641
909139
|
}
|
|
908642
909140
|
function requireScope(value) {
|
|
908643
909141
|
if (!value || !isMemoryScope2(value))
|
|
908644
|
-
throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${
|
|
909142
|
+
throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${VALID_SCOPES3.join(", ")}`);
|
|
908645
909143
|
return value;
|
|
908646
909144
|
}
|
|
908647
909145
|
function optionalScope(value) {
|
|
@@ -923926,8 +924424,6 @@ function operatorContractMethods3() {
|
|
|
923926
924424
|
function operatorMethodSearchText(method) {
|
|
923927
924425
|
return [
|
|
923928
924426
|
method.id,
|
|
923929
|
-
method.title,
|
|
923930
|
-
method.description,
|
|
923931
924427
|
method.category,
|
|
923932
924428
|
method.http?.method,
|
|
923933
924429
|
method.http?.path,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "GoodVibes personal operator assistant TUI with a proactive Agent product brain, isolated Agent Knowledge, local profiles, routines, skills, personas, and explicit build delegation.",
|
|
6
6
|
"type": "module",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
},
|
|
93
93
|
"dependencies": {},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@pellux/goodvibes-sdk": "1.
|
|
95
|
+
"@pellux/goodvibes-sdk": "1.4.0",
|
|
96
96
|
"sql.js": "^1.14.1",
|
|
97
97
|
"sqlite-vec": "^0.1.9",
|
|
98
98
|
"zustand": "^5.0.12",
|
|
@@ -88,10 +88,11 @@ function operatorContractMethods(): readonly OperatorContractMethod[] {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
function operatorMethodSearchText(method: OperatorContractMethod): string {
|
|
91
|
+
// EXCLUDES title/description prose: discovery keys off what a method IS
|
|
92
|
+
// (id/route/category/scopes), not how its docs read — SDK 1.4.0's "terminal
|
|
93
|
+
// turn.cancelled event" wording false-matched as a terminal/TTY capability.
|
|
91
94
|
return [
|
|
92
95
|
method.id,
|
|
93
|
-
method.title,
|
|
94
|
-
method.description,
|
|
95
96
|
method.category,
|
|
96
97
|
method.http?.method,
|
|
97
98
|
method.http?.path,
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '1.6.
|
|
9
|
+
let _version = '1.6.3';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
12
12
|
readonly version?: unknown;
|