@pellux/goodvibes-agent 1.6.2 → 1.6.4
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 +635 -79
- 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.4 - 2026-07-07
|
|
6
|
+
|
|
7
|
+
- The exec tool no longer denies destructive- or escalation-class commands (kill, docker, sudo) that the permission configuration allows — class-level risk is decided by permission settings alone. The only remaining unconditional block is a small frozen catastrophic list (root filesystem deletion, raw disk destruction, fork bombs).
|
|
8
|
+
|
|
9
|
+
## 1.6.3 - 2026-07-07
|
|
10
|
+
|
|
11
|
+
- 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.
|
|
12
|
+
|
|
5
13
|
## 1.6.2 - 2026-07-07
|
|
6
14
|
|
|
7
15
|
- 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.
|
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.1", 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"));
|
|
@@ -63439,6 +63439,39 @@ function collectCommandNodes(node, acc = []) {
|
|
|
63439
63439
|
}
|
|
63440
63440
|
|
|
63441
63441
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/runtime/permissions/normalization/classifier.js
|
|
63442
|
+
function catastrophicReason(seg) {
|
|
63443
|
+
const { command, args: args2, flags: flags2, raw } = seg;
|
|
63444
|
+
const operands = [
|
|
63445
|
+
...args2,
|
|
63446
|
+
...seg.tokens.filter((t) => t.type !== "command" && t.type !== "flag").map((t) => t.value)
|
|
63447
|
+
];
|
|
63448
|
+
if (command === "rm") {
|
|
63449
|
+
if (flags2.includes("--no-preserve-root")) {
|
|
63450
|
+
return "rm --no-preserve-root: destructive root filesystem deletion";
|
|
63451
|
+
}
|
|
63452
|
+
const recursive = flags2.some((f) => f.includes("r") || f === "--recursive");
|
|
63453
|
+
const force = flags2.some((f) => f.includes("f") || f === "--force");
|
|
63454
|
+
if (recursive && force && operands.some((a) => a === "/" || a === "/*")) {
|
|
63455
|
+
return "rm -rf /: destructive root filesystem deletion";
|
|
63456
|
+
}
|
|
63457
|
+
}
|
|
63458
|
+
if (command === "dd" && operands.some((a) => a.startsWith("of=/dev/"))) {
|
|
63459
|
+
return "dd writing to a raw device: destructive disk overwrite";
|
|
63460
|
+
}
|
|
63461
|
+
if (command.startsWith("mkfs") || command === "wipefs") {
|
|
63462
|
+
return `${command}: destructive filesystem/device wipe`;
|
|
63463
|
+
}
|
|
63464
|
+
if (command === "shred" && operands.some((a) => a.startsWith("/dev/"))) {
|
|
63465
|
+
return "shred on a raw device: destructive disk overwrite";
|
|
63466
|
+
}
|
|
63467
|
+
if (raw.includes(":(){") || raw.includes(":|:&")) {
|
|
63468
|
+
return "fork bomb: destructive resource exhaustion";
|
|
63469
|
+
}
|
|
63470
|
+
if (/>\s*\/dev\/(sd|hd|nvme|vd|mmcblk)/.test(raw)) {
|
|
63471
|
+
return "redirect to a raw disk device: destructive disk overwrite";
|
|
63472
|
+
}
|
|
63473
|
+
return null;
|
|
63474
|
+
}
|
|
63442
63475
|
function higherPriority(a, b) {
|
|
63443
63476
|
const ai = CLASSIFICATION_PRIORITY.indexOf(a);
|
|
63444
63477
|
const bi = CLASSIFICATION_PRIORITY.indexOf(b);
|
|
@@ -63880,7 +63913,7 @@ No parseable command segments found. Denied as a precaution.`,
|
|
|
63880
63913
|
}
|
|
63881
63914
|
return compound;
|
|
63882
63915
|
}
|
|
63883
|
-
var DEFAULT_ALLOWED_CLASSES, OBFUSCATION_CHECKS, CLASSIFICATION_PRIORITY2, DEFAULT_POLICIES;
|
|
63916
|
+
var DEFAULT_ALLOWED_CLASSES, ALL_COMMAND_CLASSES, OBFUSCATION_CHECKS, CLASSIFICATION_PRIORITY2, DEFAULT_POLICIES;
|
|
63884
63917
|
var init_verdict = __esm(() => {
|
|
63885
63918
|
init_classifier();
|
|
63886
63919
|
DEFAULT_ALLOWED_CLASSES = new Set([
|
|
@@ -63888,6 +63921,13 @@ var init_verdict = __esm(() => {
|
|
|
63888
63921
|
"write",
|
|
63889
63922
|
"network"
|
|
63890
63923
|
]);
|
|
63924
|
+
ALL_COMMAND_CLASSES = new Set([
|
|
63925
|
+
"read",
|
|
63926
|
+
"write",
|
|
63927
|
+
"network",
|
|
63928
|
+
"destructive",
|
|
63929
|
+
"escalation"
|
|
63930
|
+
]);
|
|
63891
63931
|
OBFUSCATION_CHECKS = [
|
|
63892
63932
|
{
|
|
63893
63933
|
description: "base64-encoded argument (possible command injection)",
|
|
@@ -63930,9 +63970,16 @@ var init_verdict = __esm(() => {
|
|
|
63930
63970
|
"read"
|
|
63931
63971
|
];
|
|
63932
63972
|
DEFAULT_POLICIES = [
|
|
63933
|
-
(
|
|
63934
|
-
|
|
63935
|
-
|
|
63973
|
+
(node, _cls) => {
|
|
63974
|
+
const reason = catastrophicReason({
|
|
63975
|
+
raw: node.raw,
|
|
63976
|
+
tokens: node.tokens,
|
|
63977
|
+
command: node.command,
|
|
63978
|
+
args: node.args,
|
|
63979
|
+
flags: node.flags
|
|
63980
|
+
});
|
|
63981
|
+
return reason === null ? null : `unconditionally blocked destructive command \u2014 ${reason}`;
|
|
63982
|
+
}
|
|
63936
63983
|
];
|
|
63937
63984
|
});
|
|
63938
63985
|
|
|
@@ -64004,15 +64051,27 @@ var init_normalization = __esm(() => {
|
|
|
64004
64051
|
function isASTNormalizationEnabled(flagManager) {
|
|
64005
64052
|
return flagManager?.isEnabled("shell-ast-normalization") ?? false;
|
|
64006
64053
|
}
|
|
64007
|
-
function baselineGuard(command) {
|
|
64054
|
+
function baselineGuard(command, allowedClasses) {
|
|
64008
64055
|
const normalized = normalizeCommand(command);
|
|
64056
|
+
for (const seg of normalized.segments) {
|
|
64057
|
+
const reason = catastrophicReason(seg);
|
|
64058
|
+
if (reason !== null) {
|
|
64059
|
+
return {
|
|
64060
|
+
allowed: false,
|
|
64061
|
+
denialMessage: `Command denied (safety block): "${command}"
|
|
64062
|
+
` + `Unconditionally blocked destructive command \u2014 ${reason}.
|
|
64063
|
+
` + `This block is not affected by permission settings.`,
|
|
64064
|
+
astModeActive: false
|
|
64065
|
+
};
|
|
64066
|
+
}
|
|
64067
|
+
}
|
|
64009
64068
|
const cls = normalized.highestClassification;
|
|
64010
|
-
if (cls
|
|
64069
|
+
if (!allowedClasses.has(cls)) {
|
|
64011
64070
|
return {
|
|
64012
64071
|
allowed: false,
|
|
64013
|
-
denialMessage: `Command denied (
|
|
64072
|
+
denialMessage: `Command denied (command-class policy): "${command}"
|
|
64014
64073
|
` + `Classification: ${cls}
|
|
64015
|
-
` + `
|
|
64074
|
+
` + `Classification "${cls}" is not in the caller's allowed set [${[...allowedClasses].join(", ")}].`,
|
|
64016
64075
|
astModeActive: false
|
|
64017
64076
|
};
|
|
64018
64077
|
}
|
|
@@ -64039,7 +64098,7 @@ async function guardExecCommand(command, allowedClasses = DEFAULT_ALLOWED_CLASSE
|
|
|
64039
64098
|
if (isASTNormalizationEnabled(flagManager)) {
|
|
64040
64099
|
return astGuard(command, allowedClasses);
|
|
64041
64100
|
}
|
|
64042
|
-
return baselineGuard(command);
|
|
64101
|
+
return baselineGuard(command, allowedClasses);
|
|
64043
64102
|
}
|
|
64044
64103
|
function formatDenialResponse(result, cmd) {
|
|
64045
64104
|
const segmentDetails = result.verdict?.segments.map((s) => ({
|
|
@@ -64062,6 +64121,7 @@ var init_ast_guard = __esm(() => {
|
|
|
64062
64121
|
init_parser();
|
|
64063
64122
|
init_verdict();
|
|
64064
64123
|
init_normalization();
|
|
64124
|
+
init_classifier();
|
|
64065
64125
|
});
|
|
64066
64126
|
|
|
64067
64127
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/tools/exec/file-ops.js
|
|
@@ -64434,7 +64494,7 @@ function handleBgSpecialCommand(processManager, cmd) {
|
|
|
64434
64494
|
return processManager.handleCommand(cmd);
|
|
64435
64495
|
}
|
|
64436
64496
|
async function runCommand(processManager, overflowHandler, featureFlags, cmdStr, cmdInput, workingDirectory, globalTimeout, signal) {
|
|
64437
|
-
const guardResult = await guardExecCommand(cmdStr,
|
|
64497
|
+
const guardResult = await guardExecCommand(cmdStr, ALL_COMMAND_CLASSES, featureFlags);
|
|
64438
64498
|
if (!guardResult.allowed) {
|
|
64439
64499
|
const denial = formatDenialResponse(guardResult, cmdStr);
|
|
64440
64500
|
return {
|
|
@@ -264004,8 +264064,8 @@ var FOUNDATION_METADATA;
|
|
|
264004
264064
|
var init_foundation_metadata = __esm(() => {
|
|
264005
264065
|
FOUNDATION_METADATA = {
|
|
264006
264066
|
productId: "goodvibes",
|
|
264007
|
-
productVersion: "1.
|
|
264008
|
-
operatorMethodCount:
|
|
264067
|
+
productVersion: "1.4.1",
|
|
264068
|
+
operatorMethodCount: 329,
|
|
264009
264069
|
operatorEventCount: 31,
|
|
264010
264070
|
peerEndpointCount: 6
|
|
264011
264071
|
};
|
|
@@ -264019,7 +264079,7 @@ var init_operator_contract = __esm(() => {
|
|
|
264019
264079
|
product: {
|
|
264020
264080
|
id: "goodvibes",
|
|
264021
264081
|
surface: "operator",
|
|
264022
|
-
version: "1.
|
|
264082
|
+
version: "1.4.1"
|
|
264023
264083
|
},
|
|
264024
264084
|
auth: {
|
|
264025
264085
|
modes: [
|
|
@@ -286083,6 +286143,16 @@ var init_operator_contract = __esm(() => {
|
|
|
286083
286143
|
},
|
|
286084
286144
|
createdAt: {
|
|
286085
286145
|
type: "number"
|
|
286146
|
+
},
|
|
286147
|
+
deliveryState: {
|
|
286148
|
+
type: "string",
|
|
286149
|
+
enum: [
|
|
286150
|
+
"cancelled",
|
|
286151
|
+
"queued"
|
|
286152
|
+
]
|
|
286153
|
+
},
|
|
286154
|
+
inReplyTo: {
|
|
286155
|
+
type: "string"
|
|
286086
286156
|
}
|
|
286087
286157
|
},
|
|
286088
286158
|
required: [
|
|
@@ -286161,6 +286231,99 @@ var init_operator_contract = __esm(() => {
|
|
|
286161
286231
|
},
|
|
286162
286232
|
invokable: true
|
|
286163
286233
|
},
|
|
286234
|
+
{
|
|
286235
|
+
id: "companion.chat.messages.steer",
|
|
286236
|
+
title: "Steer Companion Chat (Interrupt And Send)",
|
|
286237
|
+
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.',
|
|
286238
|
+
category: "companion",
|
|
286239
|
+
source: "builtin",
|
|
286240
|
+
access: "authenticated",
|
|
286241
|
+
transport: [
|
|
286242
|
+
"http",
|
|
286243
|
+
"ws"
|
|
286244
|
+
],
|
|
286245
|
+
scopes: [
|
|
286246
|
+
"write:sessions"
|
|
286247
|
+
],
|
|
286248
|
+
http: {
|
|
286249
|
+
method: "POST",
|
|
286250
|
+
path: "/api/companion/chat/sessions/{sessionId}/messages/steer"
|
|
286251
|
+
},
|
|
286252
|
+
inputSchema: {
|
|
286253
|
+
type: "object",
|
|
286254
|
+
properties: {
|
|
286255
|
+
sessionId: {
|
|
286256
|
+
type: "string"
|
|
286257
|
+
},
|
|
286258
|
+
body: {
|
|
286259
|
+
type: "string"
|
|
286260
|
+
},
|
|
286261
|
+
content: {
|
|
286262
|
+
type: "string"
|
|
286263
|
+
},
|
|
286264
|
+
attachments: {
|
|
286265
|
+
type: "array",
|
|
286266
|
+
items: {
|
|
286267
|
+
type: "object",
|
|
286268
|
+
properties: {
|
|
286269
|
+
artifactId: {
|
|
286270
|
+
type: "string"
|
|
286271
|
+
},
|
|
286272
|
+
label: {
|
|
286273
|
+
type: "string"
|
|
286274
|
+
},
|
|
286275
|
+
metadata: {
|
|
286276
|
+
type: "object",
|
|
286277
|
+
properties: {},
|
|
286278
|
+
additionalProperties: false
|
|
286279
|
+
}
|
|
286280
|
+
},
|
|
286281
|
+
required: [
|
|
286282
|
+
"artifactId"
|
|
286283
|
+
],
|
|
286284
|
+
additionalProperties: false
|
|
286285
|
+
}
|
|
286286
|
+
},
|
|
286287
|
+
metadata: {
|
|
286288
|
+
type: "object",
|
|
286289
|
+
properties: {},
|
|
286290
|
+
additionalProperties: false
|
|
286291
|
+
}
|
|
286292
|
+
},
|
|
286293
|
+
required: [
|
|
286294
|
+
"sessionId"
|
|
286295
|
+
],
|
|
286296
|
+
additionalProperties: true
|
|
286297
|
+
},
|
|
286298
|
+
outputSchema: {
|
|
286299
|
+
type: "object",
|
|
286300
|
+
properties: {
|
|
286301
|
+
sessionId: {
|
|
286302
|
+
type: "string"
|
|
286303
|
+
},
|
|
286304
|
+
messageId: {
|
|
286305
|
+
type: "string"
|
|
286306
|
+
},
|
|
286307
|
+
steered: {
|
|
286308
|
+
type: "boolean"
|
|
286309
|
+
},
|
|
286310
|
+
cancelledTurnId: {
|
|
286311
|
+
type: "string"
|
|
286312
|
+
},
|
|
286313
|
+
turnStarted: {
|
|
286314
|
+
type: "boolean"
|
|
286315
|
+
}
|
|
286316
|
+
},
|
|
286317
|
+
required: [
|
|
286318
|
+
"sessionId",
|
|
286319
|
+
"messageId",
|
|
286320
|
+
"steered",
|
|
286321
|
+
"turnStarted"
|
|
286322
|
+
],
|
|
286323
|
+
additionalProperties: false
|
|
286324
|
+
},
|
|
286325
|
+
invokable: true
|
|
286326
|
+
},
|
|
286164
286327
|
{
|
|
286165
286328
|
id: "companion.chat.sessions.close",
|
|
286166
286329
|
title: "Close Companion Chat Session",
|
|
@@ -286627,6 +286790,16 @@ var init_operator_contract = __esm(() => {
|
|
|
286627
286790
|
},
|
|
286628
286791
|
createdAt: {
|
|
286629
286792
|
type: "number"
|
|
286793
|
+
},
|
|
286794
|
+
deliveryState: {
|
|
286795
|
+
type: "string",
|
|
286796
|
+
enum: [
|
|
286797
|
+
"cancelled",
|
|
286798
|
+
"queued"
|
|
286799
|
+
]
|
|
286800
|
+
},
|
|
286801
|
+
inReplyTo: {
|
|
286802
|
+
type: "string"
|
|
286630
286803
|
}
|
|
286631
286804
|
},
|
|
286632
286805
|
required: [
|
|
@@ -286935,6 +287108,68 @@ var init_operator_contract = __esm(() => {
|
|
|
286935
287108
|
},
|
|
286936
287109
|
invokable: true
|
|
286937
287110
|
},
|
|
287111
|
+
{
|
|
287112
|
+
id: "companion.chat.turns.cancel",
|
|
287113
|
+
title: "Cancel Companion Chat Turn",
|
|
287114
|
+
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.',
|
|
287115
|
+
category: "companion",
|
|
287116
|
+
source: "builtin",
|
|
287117
|
+
access: "authenticated",
|
|
287118
|
+
transport: [
|
|
287119
|
+
"http",
|
|
287120
|
+
"ws"
|
|
287121
|
+
],
|
|
287122
|
+
scopes: [
|
|
287123
|
+
"write:sessions"
|
|
287124
|
+
],
|
|
287125
|
+
http: {
|
|
287126
|
+
method: "POST",
|
|
287127
|
+
path: "/api/companion/chat/sessions/{sessionId}/turns/cancel"
|
|
287128
|
+
},
|
|
287129
|
+
inputSchema: {
|
|
287130
|
+
type: "object",
|
|
287131
|
+
properties: {
|
|
287132
|
+
sessionId: {
|
|
287133
|
+
type: "string"
|
|
287134
|
+
},
|
|
287135
|
+
turnId: {
|
|
287136
|
+
type: "string"
|
|
287137
|
+
}
|
|
287138
|
+
},
|
|
287139
|
+
required: [
|
|
287140
|
+
"sessionId"
|
|
287141
|
+
],
|
|
287142
|
+
additionalProperties: true
|
|
287143
|
+
},
|
|
287144
|
+
outputSchema: {
|
|
287145
|
+
type: "object",
|
|
287146
|
+
properties: {
|
|
287147
|
+
sessionId: {
|
|
287148
|
+
type: "string"
|
|
287149
|
+
},
|
|
287150
|
+
turnId: {
|
|
287151
|
+
type: "string"
|
|
287152
|
+
},
|
|
287153
|
+
cancelled: {
|
|
287154
|
+
type: "boolean"
|
|
287155
|
+
},
|
|
287156
|
+
alreadyCancelled: {
|
|
287157
|
+
type: "boolean"
|
|
287158
|
+
},
|
|
287159
|
+
partialPersisted: {
|
|
287160
|
+
type: "boolean"
|
|
287161
|
+
}
|
|
287162
|
+
},
|
|
287163
|
+
required: [
|
|
287164
|
+
"sessionId",
|
|
287165
|
+
"turnId",
|
|
287166
|
+
"cancelled",
|
|
287167
|
+
"partialPersisted"
|
|
287168
|
+
],
|
|
287169
|
+
additionalProperties: false
|
|
287170
|
+
},
|
|
287171
|
+
invokable: true
|
|
287172
|
+
},
|
|
286938
287173
|
{
|
|
286939
287174
|
id: "config.get",
|
|
286940
287175
|
title: "Get Config",
|
|
@@ -345393,10 +345628,10 @@ var init_operator_contract = __esm(() => {
|
|
|
345393
345628
|
}
|
|
345394
345629
|
],
|
|
345395
345630
|
schemaCoverage: {
|
|
345396
|
-
methods:
|
|
345397
|
-
typedInputs:
|
|
345631
|
+
methods: 329,
|
|
345632
|
+
typedInputs: 329,
|
|
345398
345633
|
genericInputs: 0,
|
|
345399
|
-
typedOutputs:
|
|
345634
|
+
typedOutputs: 329,
|
|
345400
345635
|
genericOutputs: 0
|
|
345401
345636
|
},
|
|
345402
345637
|
eventCoverage: {
|
|
@@ -345405,8 +345640,8 @@ var init_operator_contract = __esm(() => {
|
|
|
345405
345640
|
withWireEvents: 31
|
|
345406
345641
|
},
|
|
345407
345642
|
validationCoverage: {
|
|
345408
|
-
methods:
|
|
345409
|
-
validated:
|
|
345643
|
+
methods: 329,
|
|
345644
|
+
validated: 327,
|
|
345410
345645
|
skippedGeneric: 0,
|
|
345411
345646
|
skippedUntyped: 2
|
|
345412
345647
|
}
|
|
@@ -345502,12 +345737,14 @@ var init_operator_method_ids = __esm(() => {
|
|
|
345502
345737
|
"companion.chat.messages.edit",
|
|
345503
345738
|
"companion.chat.messages.list",
|
|
345504
345739
|
"companion.chat.messages.retry",
|
|
345740
|
+
"companion.chat.messages.steer",
|
|
345505
345741
|
"companion.chat.sessions.close",
|
|
345506
345742
|
"companion.chat.sessions.create",
|
|
345507
345743
|
"companion.chat.sessions.delete",
|
|
345508
345744
|
"companion.chat.sessions.get",
|
|
345509
345745
|
"companion.chat.sessions.list",
|
|
345510
345746
|
"companion.chat.sessions.update",
|
|
345747
|
+
"companion.chat.turns.cancel",
|
|
345511
345748
|
"config.get",
|
|
345512
345749
|
"config.set",
|
|
345513
345750
|
"continuity.snapshot",
|
|
@@ -450511,7 +450748,9 @@ var init_operator_contract_schemas_runtime = __esm(() => {
|
|
|
450511
450748
|
role: COMPANION_CHAT_MESSAGE_ROLE_SCHEMA,
|
|
450512
450749
|
content: STRING_SCHEMA,
|
|
450513
450750
|
attachments: arraySchema(COMPANION_CHAT_ATTACHMENT_SCHEMA),
|
|
450514
|
-
createdAt: NUMBER_SCHEMA
|
|
450751
|
+
createdAt: NUMBER_SCHEMA,
|
|
450752
|
+
deliveryState: enumSchema(["cancelled", "queued"]),
|
|
450753
|
+
inReplyTo: STRING_SCHEMA
|
|
450515
450754
|
}, ["id", "sessionId", "role", "content", "attachments", "createdAt"]);
|
|
450516
450755
|
COMPANION_CHAT_SESSION_WITH_MESSAGES_SCHEMA = objectSchema({
|
|
450517
450756
|
session: COMPANION_CHAT_SESSION_SCHEMA,
|
|
@@ -453393,6 +453632,51 @@ var init_method_catalog_control_companion = __esm(() => {
|
|
|
453393
453632
|
turnStarted: BOOLEAN_SCHEMA
|
|
453394
453633
|
}, ["sessionId", "editedFrom", "messageId", "supersededMessageIds", "turnStarted"])
|
|
453395
453634
|
}),
|
|
453635
|
+
methodDescriptor({
|
|
453636
|
+
id: "companion.chat.messages.steer",
|
|
453637
|
+
title: "Steer Companion Chat (Interrupt And Send)",
|
|
453638
|
+
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.',
|
|
453639
|
+
category: "companion",
|
|
453640
|
+
scopes: ["write:sessions"],
|
|
453641
|
+
http: { method: "POST", path: "/api/companion/chat/sessions/{sessionId}/messages/steer" },
|
|
453642
|
+
inputSchema: bodyEnvelopeSchema({
|
|
453643
|
+
sessionId: STRING_SCHEMA,
|
|
453644
|
+
body: STRING_SCHEMA,
|
|
453645
|
+
content: STRING_SCHEMA,
|
|
453646
|
+
attachments: arraySchema(objectSchema({
|
|
453647
|
+
artifactId: STRING_SCHEMA,
|
|
453648
|
+
label: STRING_SCHEMA,
|
|
453649
|
+
metadata: objectSchema({}, [])
|
|
453650
|
+
}, ["artifactId"])),
|
|
453651
|
+
metadata: objectSchema({}, [])
|
|
453652
|
+
}, ["sessionId"]),
|
|
453653
|
+
outputSchema: objectSchema({
|
|
453654
|
+
sessionId: STRING_SCHEMA,
|
|
453655
|
+
messageId: STRING_SCHEMA,
|
|
453656
|
+
steered: BOOLEAN_SCHEMA,
|
|
453657
|
+
cancelledTurnId: STRING_SCHEMA,
|
|
453658
|
+
turnStarted: BOOLEAN_SCHEMA
|
|
453659
|
+
}, ["sessionId", "messageId", "steered", "turnStarted"])
|
|
453660
|
+
}),
|
|
453661
|
+
methodDescriptor({
|
|
453662
|
+
id: "companion.chat.turns.cancel",
|
|
453663
|
+
title: "Cancel Companion Chat Turn",
|
|
453664
|
+
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.',
|
|
453665
|
+
category: "companion",
|
|
453666
|
+
scopes: ["write:sessions"],
|
|
453667
|
+
http: { method: "POST", path: "/api/companion/chat/sessions/{sessionId}/turns/cancel" },
|
|
453668
|
+
inputSchema: bodyEnvelopeSchema({
|
|
453669
|
+
sessionId: STRING_SCHEMA,
|
|
453670
|
+
turnId: STRING_SCHEMA
|
|
453671
|
+
}, ["sessionId"]),
|
|
453672
|
+
outputSchema: objectSchema({
|
|
453673
|
+
sessionId: STRING_SCHEMA,
|
|
453674
|
+
turnId: STRING_SCHEMA,
|
|
453675
|
+
cancelled: BOOLEAN_SCHEMA,
|
|
453676
|
+
alreadyCancelled: BOOLEAN_SCHEMA,
|
|
453677
|
+
partialPersisted: BOOLEAN_SCHEMA
|
|
453678
|
+
}, ["sessionId", "turnId", "cancelled", "partialPersisted"])
|
|
453679
|
+
}),
|
|
453396
453680
|
methodDescriptor({
|
|
453397
453681
|
id: "companion.chat.events.stream",
|
|
453398
453682
|
title: "Stream Companion Chat Events",
|
|
@@ -813161,6 +813445,12 @@ async function dispatchCompanionChatRoutes(req, context) {
|
|
|
813161
813445
|
if (sub === "messages/edit" && req.method === "POST") {
|
|
813162
813446
|
return handleEditMessage(req, sessionId, context);
|
|
813163
813447
|
}
|
|
813448
|
+
if (sub === "turns/cancel" && req.method === "POST") {
|
|
813449
|
+
return handleCancelTurn(req, sessionId, context);
|
|
813450
|
+
}
|
|
813451
|
+
if (sub === "messages/steer" && req.method === "POST") {
|
|
813452
|
+
return handleSteerMessage(req, sessionId, context);
|
|
813453
|
+
}
|
|
813164
813454
|
if (sub === "events" && req.method === "GET") {
|
|
813165
813455
|
return handleGetEvents(req, sessionId, context);
|
|
813166
813456
|
}
|
|
@@ -813417,6 +813707,42 @@ async function handleGetMessages(sessionId, context) {
|
|
|
813417
813707
|
const messages = context.chatManager.getMessages(sessionId);
|
|
813418
813708
|
return Response.json({ sessionId, messages });
|
|
813419
813709
|
}
|
|
813710
|
+
async function handleCancelTurn(req, sessionId, context) {
|
|
813711
|
+
const bodyOrResponse = await context.parseOptionalJsonBody(req);
|
|
813712
|
+
if (bodyOrResponse instanceof Response)
|
|
813713
|
+
return bodyOrResponse;
|
|
813714
|
+
const body2 = bodyOrResponse ?? {};
|
|
813715
|
+
const input = {
|
|
813716
|
+
turnId: typeof body2["turnId"] === "string" && body2["turnId"].trim() ? body2["turnId"].trim() : undefined
|
|
813717
|
+
};
|
|
813718
|
+
try {
|
|
813719
|
+
return Response.json(await context.chatManager.cancelTurn(sessionId, input));
|
|
813720
|
+
} catch (err2) {
|
|
813721
|
+
return respondWithManagerError(err2);
|
|
813722
|
+
}
|
|
813723
|
+
}
|
|
813724
|
+
async function handleSteerMessage(req, sessionId, context) {
|
|
813725
|
+
const bodyOrResponse = await context.parseJsonBody(req);
|
|
813726
|
+
if (bodyOrResponse instanceof Response)
|
|
813727
|
+
return bodyOrResponse;
|
|
813728
|
+
const body2 = bodyOrResponse;
|
|
813729
|
+
const rawContent = readCompanionChatMessageBody(body2);
|
|
813730
|
+
const attachments = readCompanionChatAttachments(body2);
|
|
813731
|
+
if (attachments instanceof Response)
|
|
813732
|
+
return attachments;
|
|
813733
|
+
if (!rawContent.trim() && attachments.length === 0) {
|
|
813734
|
+
return Response.json({ error: "content, body, or attachments are required", code: "INVALID_INPUT" }, { status: 400 });
|
|
813735
|
+
}
|
|
813736
|
+
try {
|
|
813737
|
+
const result = await context.chatManager.steerMessage(sessionId, rawContent, "", {
|
|
813738
|
+
attachments,
|
|
813739
|
+
metadata: typeof body2["metadata"] === "object" && body2["metadata"] !== null ? body2["metadata"] : undefined
|
|
813740
|
+
});
|
|
813741
|
+
return Response.json(result, { status: 202 });
|
|
813742
|
+
} catch (err2) {
|
|
813743
|
+
return respondWithManagerError(err2);
|
|
813744
|
+
}
|
|
813745
|
+
}
|
|
813420
813746
|
async function handleGetEvents(req, sessionId, context) {
|
|
813421
813747
|
const session2 = context.chatManager.getSession(sessionId);
|
|
813422
813748
|
if (!session2) {
|
|
@@ -814619,7 +814945,7 @@ class HomeAssistantConversationRoutes {
|
|
|
814619
814945
|
const body2 = await this.context.parseJsonBody(req);
|
|
814620
814946
|
if (body2 instanceof Response)
|
|
814621
814947
|
return body2;
|
|
814622
|
-
return this.cancelConversation(body2);
|
|
814948
|
+
return await this.cancelConversation(body2);
|
|
814623
814949
|
}
|
|
814624
814950
|
return null;
|
|
814625
814951
|
}
|
|
@@ -814805,14 +815131,24 @@ data: ${JSON.stringify(data)}
|
|
|
814805
815131
|
}
|
|
814806
815132
|
});
|
|
814807
815133
|
}
|
|
814808
|
-
cancelConversation(body2) {
|
|
815134
|
+
async cancelConversation(body2) {
|
|
814809
815135
|
const indexed = readString27(body2.messageId ?? body2.message_id) ? this.messageIndex.get(readString27(body2.messageId ?? body2.message_id)) : undefined;
|
|
814810
815136
|
const sessionId = readString27(body2.sessionId ?? body2.session_id) ?? indexed?.sessionId;
|
|
814811
815137
|
if (!sessionId) {
|
|
814812
815138
|
return Response.json({ ok: false, error: "sessionId or known messageId is required." }, { status: 400 });
|
|
814813
815139
|
}
|
|
814814
|
-
|
|
814815
|
-
|
|
815140
|
+
if (!this.context.chatManager.getSession(sessionId)) {
|
|
815141
|
+
return Response.json({ ok: false, sessionId, error: "Unknown Home Assistant chat session." }, { status: 404 });
|
|
815142
|
+
}
|
|
815143
|
+
try {
|
|
815144
|
+
await this.context.chatManager.cancelTurn(sessionId);
|
|
815145
|
+
} catch (error51) {
|
|
815146
|
+
const code2 = error51.code;
|
|
815147
|
+
if (code2 !== "NO_ACTIVE_TURN") {
|
|
815148
|
+
return Response.json({ ok: false, sessionId, error: error51 instanceof Error ? error51.message : String(error51) }, { status: 500 });
|
|
815149
|
+
}
|
|
815150
|
+
}
|
|
815151
|
+
return Response.json({ ok: true, sessionId, status: "cancelled" });
|
|
814816
815152
|
}
|
|
814817
815153
|
parseInput(body2) {
|
|
814818
815154
|
const threadId = readString27(body2.threadId ?? body2.thread_id);
|
|
@@ -816957,6 +817293,135 @@ var init_companion_chat_rate_limiter = __esm(() => {
|
|
|
816957
817293
|
init_dist();
|
|
816958
817294
|
});
|
|
816959
817295
|
|
|
817296
|
+
// node_modules/@pellux/goodvibes-sdk/dist/platform/companion/companion-chat-turn-control.js
|
|
817297
|
+
function createTurnAbortScope(turnId, sessionSignal) {
|
|
817298
|
+
const controller = new AbortController;
|
|
817299
|
+
const onSessionAbort = () => {
|
|
817300
|
+
controller.abort();
|
|
817301
|
+
};
|
|
817302
|
+
if (sessionSignal.aborted)
|
|
817303
|
+
controller.abort();
|
|
817304
|
+
else
|
|
817305
|
+
sessionSignal.addEventListener("abort", onSessionAbort, { once: true });
|
|
817306
|
+
let settle;
|
|
817307
|
+
const settled = new Promise((resolve31) => {
|
|
817308
|
+
settle = resolve31;
|
|
817309
|
+
});
|
|
817310
|
+
const activeTurn = { turnId, controller, cancelRequested: false, settled };
|
|
817311
|
+
return {
|
|
817312
|
+
activeTurn,
|
|
817313
|
+
abortSignal: controller.signal,
|
|
817314
|
+
settle,
|
|
817315
|
+
detach: () => {
|
|
817316
|
+
sessionSignal.removeEventListener("abort", onSessionAbort);
|
|
817317
|
+
}
|
|
817318
|
+
};
|
|
817319
|
+
}
|
|
817320
|
+
function finalizeCancelledTurn(ctx) {
|
|
817321
|
+
const { sessionId, turnId } = ctx;
|
|
817322
|
+
const stoppedBy = ctx.wasCancelRequested() ? "user" : ctx.isShutdown() ? "shutdown" : "session-closed";
|
|
817323
|
+
for (const [toolCallId, toolName] of ctx.openToolCalls) {
|
|
817324
|
+
ctx.publish({
|
|
817325
|
+
type: "turn.tool_result",
|
|
817326
|
+
sessionId,
|
|
817327
|
+
turnId,
|
|
817328
|
+
toolCallId,
|
|
817329
|
+
toolName,
|
|
817330
|
+
result: "Cancelled: the turn was stopped before this tool call completed.",
|
|
817331
|
+
isError: true
|
|
817332
|
+
});
|
|
817333
|
+
}
|
|
817334
|
+
ctx.openToolCalls.clear();
|
|
817335
|
+
const assistantContent = ctx.getAssistantContent();
|
|
817336
|
+
const uncommitted = ctx.getUncommittedContent();
|
|
817337
|
+
if (uncommitted.trim()) {
|
|
817338
|
+
ctx.commitPartialToHistory(uncommitted + TURN_INTERRUPTION_NOTE);
|
|
817339
|
+
}
|
|
817340
|
+
let persistedId;
|
|
817341
|
+
let envelope;
|
|
817342
|
+
if (assistantContent.trim()) {
|
|
817343
|
+
const now4 = Date.now();
|
|
817344
|
+
ctx.persistPartial({
|
|
817345
|
+
id: ctx.assistantMessageId,
|
|
817346
|
+
sessionId,
|
|
817347
|
+
role: "assistant",
|
|
817348
|
+
content: assistantContent,
|
|
817349
|
+
attachments: [],
|
|
817350
|
+
createdAt: now4,
|
|
817351
|
+
deliveryState: "cancelled",
|
|
817352
|
+
inReplyTo: ctx.userMessageId
|
|
817353
|
+
});
|
|
817354
|
+
persistedId = ctx.assistantMessageId;
|
|
817355
|
+
envelope = {
|
|
817356
|
+
sessionId,
|
|
817357
|
+
messageId: ctx.assistantMessageId,
|
|
817358
|
+
body: assistantContent,
|
|
817359
|
+
source: "companion-chat-assistant",
|
|
817360
|
+
timestamp: now4
|
|
817361
|
+
};
|
|
817362
|
+
}
|
|
817363
|
+
ctx.publish({
|
|
817364
|
+
type: "turn.cancelled",
|
|
817365
|
+
sessionId,
|
|
817366
|
+
turnId,
|
|
817367
|
+
stoppedBy,
|
|
817368
|
+
partialPersisted: persistedId !== undefined,
|
|
817369
|
+
...persistedId !== undefined ? { assistantMessageId: persistedId, envelope } : {}
|
|
817370
|
+
});
|
|
817371
|
+
ctx.resolveReply(persistedId !== undefined ? { assistantMessageId: persistedId, response: assistantContent } : {});
|
|
817372
|
+
ctx.settle({
|
|
817373
|
+
partialPersisted: persistedId !== undefined,
|
|
817374
|
+
...persistedId !== undefined ? { assistantMessageId: persistedId } : {}
|
|
817375
|
+
});
|
|
817376
|
+
}
|
|
817377
|
+
async function cancelActiveTurn(sessionId, turn2, input) {
|
|
817378
|
+
if (!turn2) {
|
|
817379
|
+
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 });
|
|
817380
|
+
}
|
|
817381
|
+
if (input.turnId !== undefined && input.turnId !== turn2.turnId) {
|
|
817382
|
+
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 });
|
|
817383
|
+
}
|
|
817384
|
+
const alreadyCancelled = turn2.cancelRequested;
|
|
817385
|
+
if (!alreadyCancelled) {
|
|
817386
|
+
turn2.cancelRequested = true;
|
|
817387
|
+
turn2.controller.abort();
|
|
817388
|
+
}
|
|
817389
|
+
const settled = await Promise.race([
|
|
817390
|
+
turn2.settled,
|
|
817391
|
+
new Promise((resolve31) => setTimeout(resolve31, CANCEL_SETTLE_TIMEOUT_MS))
|
|
817392
|
+
]);
|
|
817393
|
+
return {
|
|
817394
|
+
sessionId,
|
|
817395
|
+
turnId: turn2.turnId,
|
|
817396
|
+
cancelled: true,
|
|
817397
|
+
...alreadyCancelled ? { alreadyCancelled: true } : {},
|
|
817398
|
+
partialPersisted: settled?.partialPersisted ?? false
|
|
817399
|
+
};
|
|
817400
|
+
}
|
|
817401
|
+
function awaitCompanionReply(timeoutMs, post, onTimeout) {
|
|
817402
|
+
let messageId = "";
|
|
817403
|
+
return new Promise((resolve31) => {
|
|
817404
|
+
const timeout = setTimeout(() => {
|
|
817405
|
+
if (messageId)
|
|
817406
|
+
onTimeout(messageId);
|
|
817407
|
+
resolve31({ messageId, error: "Timed out waiting for companion chat reply" });
|
|
817408
|
+
}, timeoutMs);
|
|
817409
|
+
timeout.unref?.();
|
|
817410
|
+
post({ resolve: resolve31, timeout }).then((id) => {
|
|
817411
|
+
messageId = id;
|
|
817412
|
+
}).catch((error51) => {
|
|
817413
|
+
clearTimeout(timeout);
|
|
817414
|
+
resolve31({
|
|
817415
|
+
messageId,
|
|
817416
|
+
error: error51 instanceof Error ? error51.message : String(error51)
|
|
817417
|
+
});
|
|
817418
|
+
});
|
|
817419
|
+
});
|
|
817420
|
+
}
|
|
817421
|
+
var TURN_INTERRUPTION_NOTE = `
|
|
817422
|
+
|
|
817423
|
+
[Interrupted: the user stopped this response here, before it was complete.]`, CANCEL_SETTLE_TIMEOUT_MS = 3000;
|
|
817424
|
+
|
|
816960
817425
|
// node_modules/@pellux/goodvibes-sdk/dist/platform/companion/companion-chat-manager.js
|
|
816961
817426
|
import { randomUUID as randomUUID43 } from "crypto";
|
|
816962
817427
|
function assertCompleteProviderModelRoute(input) {
|
|
@@ -816987,6 +817452,7 @@ class CompanionChatManager {
|
|
|
816987
817452
|
gcTimer = null;
|
|
816988
817453
|
initCompleted = false;
|
|
816989
817454
|
pendingReplies = new Map;
|
|
817455
|
+
disposed = false;
|
|
816990
817456
|
_pendingSaves = new Map;
|
|
816991
817457
|
constructor(config6) {
|
|
816992
817458
|
this.provider = config6.provider;
|
|
@@ -817140,6 +817606,13 @@ class CompanionChatManager {
|
|
|
817140
817606
|
this._brokerSync.track(sessionId, () => this._brokerSync.closeSession(sessionId));
|
|
817141
817607
|
return updated;
|
|
817142
817608
|
}
|
|
817609
|
+
async cancelTurn(sessionId, input = {}) {
|
|
817610
|
+
const session2 = this.sessions.get(sessionId);
|
|
817611
|
+
if (!session2) {
|
|
817612
|
+
throw Object.assign(new Error(`Session not found: ${sessionId}`), { code: "SESSION_NOT_FOUND", status: 404 });
|
|
817613
|
+
}
|
|
817614
|
+
return cancelActiveTurn(sessionId, session2.activeTurn ?? null, input);
|
|
817615
|
+
}
|
|
817143
817616
|
async deleteSession(sessionId) {
|
|
817144
817617
|
const session2 = this.sessions.get(sessionId);
|
|
817145
817618
|
if (!session2) {
|
|
@@ -817160,29 +817633,11 @@ class CompanionChatManager {
|
|
|
817160
817633
|
});
|
|
817161
817634
|
}
|
|
817162
817635
|
async postMessageAndWaitForReply(sessionId, content, clientId = "", options = {}) {
|
|
817163
|
-
|
|
817164
|
-
|
|
817165
|
-
|
|
817166
|
-
|
|
817167
|
-
|
|
817168
|
-
resolve31({ messageId, error: "Timed out waiting for companion chat reply" });
|
|
817169
|
-
}, options.timeoutMs ?? 120000);
|
|
817170
|
-
timeout.unref?.();
|
|
817171
|
-
this._postMessageInternal(sessionId, content, clientId, {
|
|
817172
|
-
pendingReply: { resolve: resolve31, timeout },
|
|
817173
|
-
attachments: options.attachments,
|
|
817174
|
-
...options.onTurnEvent ? { onTurnEvent: options.onTurnEvent } : {}
|
|
817175
|
-
}).then((id) => {
|
|
817176
|
-
messageId = id;
|
|
817177
|
-
}).catch((error51) => {
|
|
817178
|
-
clearTimeout(timeout);
|
|
817179
|
-
resolve31({
|
|
817180
|
-
messageId,
|
|
817181
|
-
error: error51 instanceof Error ? error51.message : String(error51)
|
|
817182
|
-
});
|
|
817183
|
-
});
|
|
817184
|
-
});
|
|
817185
|
-
return result;
|
|
817636
|
+
return awaitCompanionReply(options.timeoutMs ?? 120000, (pendingReply) => this._postMessageInternal(sessionId, content, clientId, {
|
|
817637
|
+
pendingReply,
|
|
817638
|
+
attachments: options.attachments,
|
|
817639
|
+
...options.onTurnEvent ? { onTurnEvent: options.onTurnEvent } : {}
|
|
817640
|
+
}), (messageId) => this.pendingReplies.delete(messageId));
|
|
817186
817641
|
}
|
|
817187
817642
|
async _postMessageInternal(sessionId, content, clientId, options = {}) {
|
|
817188
817643
|
const session2 = this.sessions.get(sessionId);
|
|
@@ -817199,6 +817654,7 @@ class CompanionChatManager {
|
|
|
817199
817654
|
}
|
|
817200
817655
|
const messageId = randomUUID43();
|
|
817201
817656
|
const now4 = Date.now();
|
|
817657
|
+
const queuedBehindActiveTurn = session2.activeTurn != null && options.steer !== true;
|
|
817202
817658
|
const userMsg = {
|
|
817203
817659
|
id: messageId,
|
|
817204
817660
|
sessionId,
|
|
@@ -817206,10 +817662,11 @@ class CompanionChatManager {
|
|
|
817206
817662
|
content,
|
|
817207
817663
|
attachments,
|
|
817208
817664
|
...options.metadata !== undefined ? { metadata: options.metadata } : {},
|
|
817209
|
-
createdAt: now4
|
|
817665
|
+
createdAt: now4,
|
|
817666
|
+
...queuedBehindActiveTurn ? { deliveryState: "queued" } : {}
|
|
817210
817667
|
};
|
|
817668
|
+
const providerContent = await buildProviderUserContent(content, attachments, this.artifactStore);
|
|
817211
817669
|
session2.messages.push(userMsg);
|
|
817212
|
-
session2.conversation.addUserMessage(await buildProviderUserContent(content, attachments, this.artifactStore));
|
|
817213
817670
|
session2.lastActivityAt = now4;
|
|
817214
817671
|
this._updateMeta(session2, {
|
|
817215
817672
|
messageCount: session2.messages.length,
|
|
@@ -817219,16 +817676,21 @@ class CompanionChatManager {
|
|
|
817219
817676
|
if (options.pendingReply) {
|
|
817220
817677
|
this.pendingReplies.set(messageId, options.pendingReply);
|
|
817221
817678
|
}
|
|
817222
|
-
|
|
817223
|
-
|
|
817224
|
-
|
|
817225
|
-
|
|
817226
|
-
|
|
817227
|
-
|
|
817228
|
-
|
|
817679
|
+
const entry = {
|
|
817680
|
+
userMessageId: messageId,
|
|
817681
|
+
providerContent,
|
|
817682
|
+
...options.onTurnEvent ? { onTurnEvent: options.onTurnEvent } : {}
|
|
817683
|
+
};
|
|
817684
|
+
const queue = session2.pendingTurns ??= [];
|
|
817685
|
+
if (options.steer === true)
|
|
817686
|
+
queue.unshift(entry);
|
|
817687
|
+
else
|
|
817688
|
+
queue.push(entry);
|
|
817689
|
+
this._startNextTurn(session2);
|
|
817229
817690
|
return messageId;
|
|
817230
817691
|
}
|
|
817231
817692
|
dispose() {
|
|
817693
|
+
this.disposed = true;
|
|
817232
817694
|
if (this.gcTimer) {
|
|
817233
817695
|
clearInterval(this.gcTimer);
|
|
817234
817696
|
this.gcTimer = null;
|
|
@@ -817238,11 +817700,70 @@ class CompanionChatManager {
|
|
|
817238
817700
|
}
|
|
817239
817701
|
this.sessions.clear();
|
|
817240
817702
|
}
|
|
817703
|
+
async steerMessage(sessionId, content, clientId = "", options = {}) {
|
|
817704
|
+
const activeBefore = this.sessions.get(sessionId)?.activeTurn ?? null;
|
|
817705
|
+
const messageId = await this._postMessageInternal(sessionId, content, clientId, {
|
|
817706
|
+
attachments: options.attachments,
|
|
817707
|
+
metadata: options.metadata,
|
|
817708
|
+
steer: true
|
|
817709
|
+
});
|
|
817710
|
+
let cancelledTurnId;
|
|
817711
|
+
if (activeBefore) {
|
|
817712
|
+
try {
|
|
817713
|
+
const result = await this.cancelTurn(sessionId, { turnId: activeBefore.turnId });
|
|
817714
|
+
cancelledTurnId = result.turnId;
|
|
817715
|
+
} catch (err2) {
|
|
817716
|
+
const code2 = err2.code;
|
|
817717
|
+
if (code2 !== "NO_ACTIVE_TURN" && code2 !== "TURN_MISMATCH")
|
|
817718
|
+
throw err2;
|
|
817719
|
+
}
|
|
817720
|
+
}
|
|
817721
|
+
const session2 = this.sessions.get(sessionId);
|
|
817722
|
+
if (session2)
|
|
817723
|
+
this._startNextTurn(session2);
|
|
817724
|
+
return {
|
|
817725
|
+
sessionId,
|
|
817726
|
+
messageId,
|
|
817727
|
+
steered: true,
|
|
817728
|
+
...cancelledTurnId !== undefined ? { cancelledTurnId } : {},
|
|
817729
|
+
turnStarted: true
|
|
817730
|
+
};
|
|
817731
|
+
}
|
|
817732
|
+
_startNextTurn(session2) {
|
|
817733
|
+
if (session2.activeTurn || session2.meta.status === "closed")
|
|
817734
|
+
return;
|
|
817735
|
+
const next = session2.pendingTurns?.shift();
|
|
817736
|
+
if (!next)
|
|
817737
|
+
return;
|
|
817738
|
+
const idx = session2.messages.findIndex((m3) => m3.id === next.userMessageId);
|
|
817739
|
+
const queuedMsg = idx >= 0 ? session2.messages[idx] : undefined;
|
|
817740
|
+
if (queuedMsg?.deliveryState === "queued") {
|
|
817741
|
+
const { deliveryState: _cleared, ...delivered } = queuedMsg;
|
|
817742
|
+
session2.messages[idx] = delivered;
|
|
817743
|
+
this._persist(session2.meta.id);
|
|
817744
|
+
}
|
|
817745
|
+
session2.conversation.addUserMessage(next.providerContent);
|
|
817746
|
+
this._runTurn(session2, next.userMessageId, next.onTurnEvent).catch((error51) => {
|
|
817747
|
+
logger.warn("[companion-chat] turn execution failed", {
|
|
817748
|
+
sessionId: session2.meta.id,
|
|
817749
|
+
messageId: next.userMessageId,
|
|
817750
|
+
error: summarizeError(error51)
|
|
817751
|
+
});
|
|
817752
|
+
});
|
|
817753
|
+
}
|
|
817241
817754
|
async _runTurn(session2, userMessageId, onTurnEvent) {
|
|
817242
817755
|
const turnId = randomUUID43();
|
|
817243
817756
|
const sessionId = session2.meta.id;
|
|
817244
|
-
const
|
|
817757
|
+
const scope = createTurnAbortScope(turnId, session2.abortController.signal);
|
|
817758
|
+
const abortSignal = scope.abortSignal;
|
|
817759
|
+
session2.activeTurn = scope.activeTurn;
|
|
817760
|
+
const openToolCalls = new Map;
|
|
817245
817761
|
const publish = (event) => {
|
|
817762
|
+
if (event.type === "turn.tool_call" && event.toolCallId) {
|
|
817763
|
+
openToolCalls.set(event.toolCallId, event.toolName);
|
|
817764
|
+
} else if (event.type === "turn.tool_result" && event.toolCallId) {
|
|
817765
|
+
openToolCalls.delete(event.toolCallId);
|
|
817766
|
+
}
|
|
817246
817767
|
this.eventPublisher.publishEvent(`companion-chat.${event.type}`, event, session2.subscriberClientId ? { clientId: session2.subscriberClientId } : undefined);
|
|
817247
817768
|
try {
|
|
817248
817769
|
onTurnEvent?.(event);
|
|
@@ -817259,7 +817780,31 @@ class CompanionChatManager {
|
|
|
817259
817780
|
};
|
|
817260
817781
|
publish({ type: "turn.started", sessionId, messageId: userMessageId, turnId, envelope: startEnvelope });
|
|
817261
817782
|
let assistantContent = "";
|
|
817783
|
+
let uncommittedContent = "";
|
|
817262
817784
|
const assistantMessageId = randomUUID43();
|
|
817785
|
+
const finalizeCancelled = () => finalizeCancelledTurn({
|
|
817786
|
+
sessionId,
|
|
817787
|
+
turnId,
|
|
817788
|
+
assistantMessageId,
|
|
817789
|
+
userMessageId,
|
|
817790
|
+
getAssistantContent: () => assistantContent,
|
|
817791
|
+
getUncommittedContent: () => uncommittedContent,
|
|
817792
|
+
commitPartialToHistory: (content) => {
|
|
817793
|
+
session2.conversation.addAssistantMessage(content);
|
|
817794
|
+
},
|
|
817795
|
+
openToolCalls,
|
|
817796
|
+
wasCancelRequested: () => scope.activeTurn.cancelRequested,
|
|
817797
|
+
isShutdown: () => this.disposed,
|
|
817798
|
+
publish,
|
|
817799
|
+
persistPartial: (message) => {
|
|
817800
|
+
session2.messages.push(message);
|
|
817801
|
+
session2.lastActivityAt = message.createdAt;
|
|
817802
|
+
this._updateMeta(session2, { messageCount: session2.messages.length, updatedAt: message.createdAt });
|
|
817803
|
+
this._persist(sessionId);
|
|
817804
|
+
},
|
|
817805
|
+
resolveReply: (extra) => this.resolvePendingReply(userMessageId, { messageId: userMessageId, ...extra, error: "Turn cancelled" }),
|
|
817806
|
+
settle: scope.settle
|
|
817807
|
+
});
|
|
817263
817808
|
try {
|
|
817264
817809
|
const toolDefinitions = this.toolRegistry?.getToolDefinitions() ?? [];
|
|
817265
817810
|
let completed = false;
|
|
@@ -817271,7 +817816,7 @@ class CompanionChatManager {
|
|
|
817271
817816
|
tools: toolDefinitions,
|
|
817272
817817
|
abortSignal
|
|
817273
817818
|
});
|
|
817274
|
-
|
|
817819
|
+
uncommittedContent = "";
|
|
817275
817820
|
const toolCalls = [];
|
|
817276
817821
|
for await (const chunk of stream6) {
|
|
817277
817822
|
if (abortSignal.aborted)
|
|
@@ -817279,7 +817824,7 @@ class CompanionChatManager {
|
|
|
817279
817824
|
switch (chunk.type) {
|
|
817280
817825
|
case "text_delta": {
|
|
817281
817826
|
const delta = chunk.delta ?? "";
|
|
817282
|
-
|
|
817827
|
+
uncommittedContent += delta;
|
|
817283
817828
|
assistantContent += delta;
|
|
817284
817829
|
publish({ type: "turn.delta", sessionId, turnId, delta });
|
|
817285
817830
|
break;
|
|
@@ -817323,11 +817868,13 @@ class CompanionChatManager {
|
|
|
817323
817868
|
if (abortSignal.aborted)
|
|
817324
817869
|
break;
|
|
817325
817870
|
if (toolCalls.length === 0) {
|
|
817326
|
-
session2.conversation.addAssistantMessage(
|
|
817871
|
+
session2.conversation.addAssistantMessage(uncommittedContent);
|
|
817872
|
+
uncommittedContent = "";
|
|
817327
817873
|
completed = true;
|
|
817328
817874
|
break;
|
|
817329
817875
|
}
|
|
817330
|
-
session2.conversation.addAssistantMessage(
|
|
817876
|
+
session2.conversation.addAssistantMessage(uncommittedContent, { toolCalls });
|
|
817877
|
+
uncommittedContent = "";
|
|
817331
817878
|
if (!this.toolRegistry) {
|
|
817332
817879
|
completed = true;
|
|
817333
817880
|
break;
|
|
@@ -817349,7 +817896,7 @@ class CompanionChatManager {
|
|
|
817349
817896
|
}
|
|
817350
817897
|
}
|
|
817351
817898
|
if (abortSignal.aborted) {
|
|
817352
|
-
|
|
817899
|
+
finalizeCancelled();
|
|
817353
817900
|
return;
|
|
817354
817901
|
}
|
|
817355
817902
|
const now4 = Date.now();
|
|
@@ -817359,7 +817906,8 @@ class CompanionChatManager {
|
|
|
817359
817906
|
role: "assistant",
|
|
817360
817907
|
content: assistantContent,
|
|
817361
817908
|
attachments: [],
|
|
817362
|
-
createdAt: now4
|
|
817909
|
+
createdAt: now4,
|
|
817910
|
+
inReplyTo: userMessageId
|
|
817363
817911
|
};
|
|
817364
817912
|
session2.messages.push(assistantMsg);
|
|
817365
817913
|
session2.lastActivityAt = now4;
|
|
@@ -817380,8 +817928,14 @@ class CompanionChatManager {
|
|
|
817380
817928
|
publish({ type: "turn.error", sessionId, turnId, error: errorMessage });
|
|
817381
817929
|
this.resolvePendingReply(userMessageId, { messageId: userMessageId, error: errorMessage });
|
|
817382
817930
|
} else {
|
|
817383
|
-
|
|
817931
|
+
finalizeCancelled();
|
|
817384
817932
|
}
|
|
817933
|
+
} finally {
|
|
817934
|
+
if (session2.activeTurn === scope.activeTurn)
|
|
817935
|
+
session2.activeTurn = null;
|
|
817936
|
+
scope.detach();
|
|
817937
|
+
scope.settle({ partialPersisted: false });
|
|
817938
|
+
this._startNextTurn(session2);
|
|
817385
817939
|
}
|
|
817386
817940
|
}
|
|
817387
817941
|
regenerateMessage(sessionId, input = {}) {
|
|
@@ -837066,7 +837620,7 @@ var createStyledCell = (char, overrides = {}) => ({
|
|
|
837066
837620
|
// src/version.ts
|
|
837067
837621
|
import { readFileSync } from "fs";
|
|
837068
837622
|
import { join } from "path";
|
|
837069
|
-
var _version = "1.6.
|
|
837623
|
+
var _version = "1.6.4";
|
|
837070
837624
|
try {
|
|
837071
837625
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf-8"));
|
|
837072
837626
|
_version = typeof pkg.version === "string" ? pkg.version : _version;
|
|
@@ -869724,7 +870278,11 @@ function createBrowserKnowledgeSdkFromRoutes(knowledgeRoutes, options = {}) {
|
|
|
869724
870278
|
},
|
|
869725
870279
|
messages: {
|
|
869726
870280
|
create: (id, input) => invoke("companion.chat.messages.create", { sessionId: id, ...input }),
|
|
869727
|
-
list: (id) => invoke("companion.chat.messages.list", { sessionId: id })
|
|
870281
|
+
list: (id) => invoke("companion.chat.messages.list", { sessionId: id }),
|
|
870282
|
+
steer: (id, input) => invoke("companion.chat.messages.steer", { sessionId: id, ...input })
|
|
870283
|
+
},
|
|
870284
|
+
turns: {
|
|
870285
|
+
cancel: (id, input) => invoke("companion.chat.turns.cancel", { sessionId: id, ...input ?? {} })
|
|
869728
870286
|
},
|
|
869729
870287
|
events: {
|
|
869730
870288
|
stream: (id, handlers, options2) => sdk.streams.open("/api/companion/chat/sessions/" + id + "/events", handlers, options2)
|
|
@@ -894274,13 +894832,13 @@ init_state();
|
|
|
894274
894832
|
|
|
894275
894833
|
// src/input/commands/recall-shared.ts
|
|
894276
894834
|
var VALID_CLASSES = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
|
|
894277
|
-
var
|
|
894835
|
+
var VALID_SCOPES = ["session", "project", "team"];
|
|
894278
894836
|
var VALID_REVIEW_STATES = ["fresh", "reviewed", "stale", "contradicted"];
|
|
894279
894837
|
function isValidClass(s4) {
|
|
894280
894838
|
return VALID_CLASSES.includes(s4);
|
|
894281
894839
|
}
|
|
894282
894840
|
function isValidScope(s4) {
|
|
894283
|
-
return
|
|
894841
|
+
return VALID_SCOPES.includes(s4);
|
|
894284
894842
|
}
|
|
894285
894843
|
function isValidReviewState(s4) {
|
|
894286
894844
|
return VALID_REVIEW_STATES.includes(s4);
|
|
@@ -894323,7 +894881,7 @@ function handleRecallSearch(args2, context) {
|
|
|
894323
894881
|
if (isValidScope(scope))
|
|
894324
894882
|
filter.scope = scope;
|
|
894325
894883
|
else {
|
|
894326
|
-
context.print(`[memory] Unknown scope "${scope}". Valid values ${
|
|
894884
|
+
context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894327
894885
|
return;
|
|
894328
894886
|
}
|
|
894329
894887
|
}
|
|
@@ -894498,7 +895056,7 @@ function handleRecallList(args2, context) {
|
|
|
894498
895056
|
if (scopeIdx !== -1 && args2[scopeIdx + 1]) {
|
|
894499
895057
|
const scope = args2[scopeIdx + 1];
|
|
894500
895058
|
if (!isValidScope(scope)) {
|
|
894501
|
-
context.print(`[memory] Unknown scope "${scope}". Valid values ${
|
|
895059
|
+
context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894502
895060
|
return;
|
|
894503
895061
|
}
|
|
894504
895062
|
filter.scope = scope;
|
|
@@ -894548,7 +895106,7 @@ async function handleRecallAdd(args2, context) {
|
|
|
894548
895106
|
const tags = tagsRaw ? tagsRaw.split(",").map((token) => token.trim()).filter(Boolean) : [];
|
|
894549
895107
|
const scope = scopeRaw && isValidScope(scopeRaw) ? scopeRaw : "project";
|
|
894550
895108
|
if (scopeRaw && !isValidScope(scopeRaw)) {
|
|
894551
|
-
context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${
|
|
895109
|
+
context.print(`[memory] Invalid scope "${scopeRaw}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894552
895110
|
return;
|
|
894553
895111
|
}
|
|
894554
895112
|
const provenance = [];
|
|
@@ -894683,7 +895241,7 @@ function handleRecallExport(args2, context) {
|
|
|
894683
895241
|
if (scopeIdx !== -1 && commandArgs[scopeIdx + 1]) {
|
|
894684
895242
|
const scope = commandArgs[scopeIdx + 1];
|
|
894685
895243
|
if (!isValidScope(scope)) {
|
|
894686
|
-
context.print(`[memory] Unknown scope "${scope}". Valid values ${
|
|
895244
|
+
context.print(`[memory] Unknown scope "${scope}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894687
895245
|
return;
|
|
894688
895246
|
}
|
|
894689
895247
|
filter.scope = scope;
|
|
@@ -894762,7 +895320,7 @@ function handleRecallHandoffExport(args2, context) {
|
|
|
894762
895320
|
const scopeIdx = commandArgs.indexOf("--scope");
|
|
894763
895321
|
const scopeRaw = scopeIdx !== -1 ? commandArgs[scopeIdx + 1] : "team";
|
|
894764
895322
|
if (!scopeRaw || !isValidScope(scopeRaw)) {
|
|
894765
|
-
context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${
|
|
895323
|
+
context.print(`[memory] Unknown scope "${scopeRaw ?? ""}". Valid values ${VALID_SCOPES.join(", ")}.`);
|
|
894766
895324
|
return;
|
|
894767
895325
|
}
|
|
894768
895326
|
const bundle = memory.exportBundle({ scope: scopeRaw });
|
|
@@ -894878,7 +895436,7 @@ function handleRecallPromote(args2, context) {
|
|
|
894878
895436
|
const id = parsed.rest[0];
|
|
894879
895437
|
const scope = parsed.rest[1];
|
|
894880
895438
|
if (!id || !scope || !isValidScope(scope)) {
|
|
894881
|
-
context.print(`[memory] Usage: /memory promote <id> <${
|
|
895439
|
+
context.print(`[memory] Usage: /memory promote <id> <${VALID_SCOPES.join("|")}> --yes`);
|
|
894882
895440
|
return;
|
|
894883
895441
|
}
|
|
894884
895442
|
if (!parsed.yes) {
|
|
@@ -908468,7 +909026,7 @@ async function tryWireMemoryCommand(runtime2, normalized, rest) {
|
|
|
908468
909026
|
|
|
908469
909027
|
// src/cli/memory-command.ts
|
|
908470
909028
|
var VALID_CLASSES2 = ["decision", "constraint", "incident", "pattern", "fact", "risk", "runbook", "architecture", "ownership"];
|
|
908471
|
-
var
|
|
909029
|
+
var VALID_SCOPES3 = ["session", "project", "team"];
|
|
908472
909030
|
var VALID_REVIEW_STATES3 = ["fresh", "reviewed", "stale", "contradicted"];
|
|
908473
909031
|
var VALID_PROVENANCE_KINDS = ["session", "turn", "task", "event", "file"];
|
|
908474
909032
|
var VALUE_OPTIONS = new Set([
|
|
@@ -908568,7 +909126,7 @@ function isMemoryClass2(value) {
|
|
|
908568
909126
|
return VALID_CLASSES2.includes(value);
|
|
908569
909127
|
}
|
|
908570
909128
|
function isMemoryScope2(value) {
|
|
908571
|
-
return
|
|
909129
|
+
return VALID_SCOPES3.includes(value);
|
|
908572
909130
|
}
|
|
908573
909131
|
function isReviewState(value) {
|
|
908574
909132
|
return VALID_REVIEW_STATES3.includes(value);
|
|
@@ -908583,7 +909141,7 @@ function requireClass(value) {
|
|
|
908583
909141
|
}
|
|
908584
909142
|
function requireScope(value) {
|
|
908585
909143
|
if (!value || !isMemoryScope2(value))
|
|
908586
|
-
throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${
|
|
909144
|
+
throw new Error(`Invalid memory scope "${value ?? ""}". Valid values ${VALID_SCOPES3.join(", ")}`);
|
|
908587
909145
|
return value;
|
|
908588
909146
|
}
|
|
908589
909147
|
function optionalScope(value) {
|
|
@@ -923868,8 +924426,6 @@ function operatorContractMethods3() {
|
|
|
923868
924426
|
function operatorMethodSearchText(method) {
|
|
923869
924427
|
return [
|
|
923870
924428
|
method.id,
|
|
923871
|
-
method.title,
|
|
923872
|
-
method.description,
|
|
923873
924429
|
method.category,
|
|
923874
924430
|
method.http?.method,
|
|
923875
924431
|
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.4",
|
|
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.1",
|
|
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.4';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
12
12
|
readonly version?: unknown;
|