@pellux/goodvibes-agent 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +47 -2
- package/dist/package/main.js +1883 -743
- package/package.json +2 -2
- package/release/live-verification/live-verification.json +13 -13
- package/release/live-verification/live-verification.md +15 -15
- package/release/release-notes.md +5 -11
- package/src/input/shell-passthrough.ts +58 -0
- package/src/main.ts +18 -1
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-agent",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
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",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@pellux/goodvibes-sdk": "0.33.
|
|
93
|
+
"@pellux/goodvibes-sdk": "0.33.38",
|
|
94
94
|
"sql.js": "^1.14.1",
|
|
95
95
|
"sqlite-vec": "^0.1.9",
|
|
96
96
|
"zustand": "^5.0.12",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-06-
|
|
2
|
+
"generatedAt": "2026-06-14T21:34:04.442Z",
|
|
3
3
|
"homeDir": "[goodvibes-home]",
|
|
4
4
|
"binaryPath": "[agent-binary]",
|
|
5
5
|
"connectedHostBaseUrl": "http://127.0.0.1:3421",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"id": "verification-ledger",
|
|
10
10
|
"title": "Verification inventory ledger",
|
|
11
11
|
"status": "pass",
|
|
12
|
-
"summary": "
|
|
13
|
-
"detail": "
|
|
12
|
+
"summary": "99.5% local verification signal across 806 inventory items.",
|
|
13
|
+
"detail": "83.9% local behavior verified; 96 item(s) require external outcomes."
|
|
14
14
|
},
|
|
15
15
|
{
|
|
16
16
|
"id": "compiled-cli-present",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"title": "Agent CLI version command",
|
|
24
24
|
"status": "pass",
|
|
25
25
|
"summary": "Agent CLI version returned successfully.",
|
|
26
|
-
"detail": "goodvibes-agent 1.0
|
|
26
|
+
"detail": "goodvibes-agent 1.5.0"
|
|
27
27
|
},
|
|
28
28
|
{
|
|
29
29
|
"id": "cli-status-json",
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"title": "Agent CLI compatibility JSON command",
|
|
38
38
|
"status": "pass",
|
|
39
39
|
"summary": "Agent CLI compatibility returned parseable JSON.",
|
|
40
|
-
"detail": "{\n \"ok\": true,\n \"packageVersion\": \"1.0
|
|
40
|
+
"detail": "{\n \"ok\": true,\n \"packageVersion\": \"1.5.0\",\n \"connectedHost\": {\n \"baseUrl\": \"http://127.0.0.1:3421\",\n \"status\": 200,\n \"reachable\": true,\n \"compatible\": true\n },\n \"auth\": {\n \"tokenPresent\": true,\n \"tokenPath\": \"env:GOODVIBES_CONNECTED_HOST_TOKEN\"\n },\n \"agentKnowledge\": {\n \"route\": \"/api/goodvibes-agent/knowledge/status\",\n \"ready\": true,\n \"kind\": \"ok\"\n }\n}"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"id": "cli-agent-knowledge-status",
|
|
44
44
|
"title": "Agent Knowledge CLI status command",
|
|
45
45
|
"status": "pass",
|
|
46
46
|
"summary": "Agent Knowledge status returned parseable JSON.",
|
|
47
|
-
"detail": "{\n \"ok\": true,\n \"kind\": \"agentKnowledge.status\",\n \"route\": \"/api/goodvibes-agent/knowledge/status\",\n \"data\": {\n \"ready\": true,\n \"storagePath\": \"[goodvibes-home]/tui/knowledge-agent.sqlite\",\n \"sourceCount\": 0,\n \"nodeCount\":
|
|
47
|
+
"detail": "{\n \"ok\": true,\n \"kind\": \"agentKnowledge.status\",\n \"route\": \"/api/goodvibes-agent/knowledge/status\",\n \"data\": {\n \"ready\": true,\n \"storagePath\": \"[goodvibes-home]/tui/knowledge-agent.sqlite\",\n \"sourceCount\": 0,\n \"nodeCount\": 0,\n \"edgeCount\": 0,\n \"issueCount\": 0,\n \"extractionCount\": 0,\n \"jobRunCount\": 0,\n \"refinementTaskCount\": 0,\n \"usageCount\": 0,\n \"candidateCount\": 0,\n \"reportCount\": 0,\n \"scheduleCount\": 3,\n \"note\": \"Structured knowledge uses SQL-backed sources, nodes, edges, issues, extractions, and job runs. Markdown is an optional projection, not the source of truth.\"\n }\n}"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"id": "cli-providers",
|
|
@@ -65,28 +65,28 @@
|
|
|
65
65
|
"title": "Authenticated connected-host /status",
|
|
66
66
|
"status": "pass",
|
|
67
67
|
"summary": "/status returned 200 with parseable JSON.",
|
|
68
|
-
"detail": "{\"status\":\"running\"}"
|
|
68
|
+
"detail": "{\"status\":\"running\",\"version\":\"0.33.36\"}"
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"id": "connected-host-health",
|
|
72
72
|
"title": "Authenticated connected-host /api/health",
|
|
73
73
|
"status": "pass",
|
|
74
74
|
"summary": "Health overall=healthy.",
|
|
75
|
-
"detail": "{\"overall\":\"healthy\",\"degradedDomains\":[],\"providerProblems\":[],\"mcpProblems\":{\"degraded\":[],\"quarantined\":[]},\"integrationProblems\":[],\"network\":{\"controlPlane\":{\"surface\":\"controlPlane\",\"host\":\"
|
|
75
|
+
"detail": "{\"overall\":\"healthy\",\"degradedDomains\":[],\"providerProblems\":[],\"mcpProblems\":{\"degraded\":[],\"quarantined\":[]},\"integrationProblems\":[],\"network\":{\"controlPlane\":{\"surface\":\"controlPlane\",\"host\":\"127.0.0.1\",\"port\":3421,\"mode\":\"off\",\"scheme\":\"http\",\"trustProxy\":false,\"usingDefaultPaths\":false,\"ready\":true,\"errors\":[]},\"httpListener\":{\"surface\":\"httpListener\",\"host\":\"127.0.0.1\",\"port\":3422,\"mode\":\"off\",\"scheme\":\"http\",\"trustProxy\":false,\"usingDefaultPaths\":false,\"ready\":true,\"errors\":[]},\"outbound\":{\"mode\":\"bundled\",\"allowInsecureLocalhost\":false,\"customCaEntryCount\":0,\"effectiveCaStrategy\":\"bun-default\",\"errors\":[]}}}"
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
78
|
"id": "openai-compatible-models",
|
|
79
79
|
"title": "OpenAI-compatible /v1/models route",
|
|
80
80
|
"status": "pass",
|
|
81
|
-
"summary": "/v1/models returned
|
|
82
|
-
"detail": "/v1/models returned
|
|
81
|
+
"summary": "/v1/models returned 175 model(s).",
|
|
82
|
+
"detail": "/v1/models returned 175 model(s); model identifiers omitted from release artifact."
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"id": "agent-knowledge-status",
|
|
86
86
|
"title": "Agent Knowledge isolated /status",
|
|
87
87
|
"status": "pass",
|
|
88
88
|
"summary": "Agent Knowledge status route returned parseable isolated JSON.",
|
|
89
|
-
"detail": "{\"ready\":true,\"storagePath\":\"[goodvibes-home]/tui/knowledge-agent.sqlite\",\"sourceCount\":0,\"nodeCount\":
|
|
89
|
+
"detail": "{\"ready\":true,\"storagePath\":\"[goodvibes-home]/tui/knowledge-agent.sqlite\",\"sourceCount\":0,\"nodeCount\":0,\"edgeCount\":0,\"issueCount\":0,\"extractionCount\":0,\"jobRunCount\":0,\"refinementTaskCount\":0,\"usageCount\":0,\"candidateCount\":0,\"reportCount\":0,\"scheduleCount\":3,\"note\":\"Structured knowledge uses SQL-backed sources, nodes, edges, issues, extractions, and job runs. Markdown is an optional projection, not the source of truth.\"}"
|
|
90
90
|
},
|
|
91
91
|
{
|
|
92
92
|
"id": "agent-knowledge-ask-isolated",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"title": "Agent Knowledge isolated nodes list",
|
|
115
115
|
"status": "pass",
|
|
116
116
|
"summary": "Agent Knowledge isolated nodes list stayed on the isolated Agent route.",
|
|
117
|
-
"detail": "{\"nodes\":[
|
|
117
|
+
"detail": "{\"nodes\":[]}"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
"id": "agent-knowledge-issues-isolated",
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"title": "Agent Knowledge isolated map",
|
|
129
129
|
"status": "pass",
|
|
130
130
|
"summary": "Agent Knowledge isolated map stayed on the isolated Agent route.",
|
|
131
|
-
"detail": "{\"ok\":true,\"title\":\"Knowledge Map\",\"generatedAt\":
|
|
131
|
+
"detail": "{\"ok\":true,\"title\":\"Knowledge Map\",\"generatedAt\":1781472844439,\"width\":1280,\"height\":920,\"nodeCount\":0,\"edgeCount\":0,\"totalNodeCount\":0,\"totalEdgeCount\":0,\"facets\":{\"recordKinds\":[],\"nodeKinds\":[],\"sourceTypes\":[],\"sourceStatuses\":[],\"nodeStatuses\":[],\"issueCodes\":[],\"issueStatuses\":[],\"issueSeverities\":[],\"edgeRelations\":[],\"tags\":[]},\"nodes\":[],\"edges\":[],\"svg\":\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\" width=\\\"1280\\\" height=\\\"920\\\" viewBox=\\\"0 0 1280 920\\\" role=\\\"img\\\" aria-label=\\\"Knowledge Map\\\">\\n<defs>\\n <radialGradient id=\\\"knowledgeMapBg\\\" cx=\\\"50%\\\" cy=\\\"46%\\\" r=\\\"70%\\\">\\n <stop offset=\\\"0%\\\" stop-color=\\\"#f7f4ec\\\" />\\n <stop offset=\\\"60%\\\" stop-color=\\\"#e9eef0\\\" />\\n <stop offset=\\\"100%\\\" stop-color=\\\"#dde6df\\\" />\\n </radialGradient>\\n <filter id=\\\"softShadow\\\" x=\\\"-20%\\\" y=\\\"-20%\\\" width=\\\"140%\\\" height=\\\"140%\\\">\\n <feDropShadow dx=\\\"0\\\" dy=\\\"6\\... [truncated]"
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
"id": "agent-knowledge-connectors-isolated",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GoodVibes Agent Live Verification
|
|
2
2
|
|
|
3
|
-
Generated: 2026-06-
|
|
3
|
+
Generated: 2026-06-14T21:34:04.442Z
|
|
4
4
|
Home: `[goodvibes-home]`
|
|
5
5
|
Binary: `[agent-binary]`
|
|
6
6
|
Connected host: `http://127.0.0.1:3421`
|
|
@@ -14,7 +14,7 @@ Connected host: `http://127.0.0.1:3421`
|
|
|
14
14
|
|
|
15
15
|
| Check | Status | Summary |
|
|
16
16
|
|---|---|---|
|
|
17
|
-
| Verification inventory ledger | pass |
|
|
17
|
+
| Verification inventory ledger | pass | 99.5% local verification signal across 806 inventory items. |
|
|
18
18
|
| Compiled GoodVibes Agent CLI binary | pass | Found [agent-binary]. |
|
|
19
19
|
| Agent CLI version command | pass | Agent CLI version returned successfully. |
|
|
20
20
|
| Agent CLI status JSON command | pass | Agent CLI status returned parseable JSON. |
|
|
@@ -24,7 +24,7 @@ Connected host: `http://127.0.0.1:3421`
|
|
|
24
24
|
| CLI doctor command | pass | Doctor completed without findings. |
|
|
25
25
|
| Authenticated connected-host /status | pass | /status returned 200 with parseable JSON. |
|
|
26
26
|
| Authenticated connected-host /api/health | pass | Health overall=healthy. |
|
|
27
|
-
| OpenAI-compatible /v1/models route | pass | /v1/models returned
|
|
27
|
+
| OpenAI-compatible /v1/models route | pass | /v1/models returned 175 model(s). |
|
|
28
28
|
| Agent Knowledge isolated /status | pass | Agent Knowledge status route returned parseable isolated JSON. |
|
|
29
29
|
| Agent Knowledge isolated ask | pass | Agent Knowledge ask stayed on the isolated Agent route. |
|
|
30
30
|
| Agent Knowledge isolated search | pass | Agent Knowledge search stayed on the isolated Agent route. |
|
|
@@ -39,13 +39,13 @@ Connected host: `http://127.0.0.1:3421`
|
|
|
39
39
|
### Verification inventory ledger
|
|
40
40
|
|
|
41
41
|
```text
|
|
42
|
-
|
|
42
|
+
83.9% local behavior verified; 96 item(s) require external outcomes.
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
### Agent CLI version command
|
|
46
46
|
|
|
47
47
|
```text
|
|
48
|
-
goodvibes-agent 1.0
|
|
48
|
+
goodvibes-agent 1.5.0
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
### Agent CLI status JSON command
|
|
@@ -59,7 +59,7 @@ Status JSON command completed; provider/model identifiers omitted from release a
|
|
|
59
59
|
```text
|
|
60
60
|
{
|
|
61
61
|
"ok": true,
|
|
62
|
-
"packageVersion": "1.0
|
|
62
|
+
"packageVersion": "1.5.0",
|
|
63
63
|
"connectedHost": {
|
|
64
64
|
"baseUrl": "http://127.0.0.1:3421",
|
|
65
65
|
"status": 200,
|
|
@@ -89,15 +89,15 @@ Status JSON command completed; provider/model identifiers omitted from release a
|
|
|
89
89
|
"ready": true,
|
|
90
90
|
"storagePath": "[goodvibes-home]/tui/knowledge-agent.sqlite",
|
|
91
91
|
"sourceCount": 0,
|
|
92
|
-
"nodeCount":
|
|
92
|
+
"nodeCount": 0,
|
|
93
93
|
"edgeCount": 0,
|
|
94
94
|
"issueCount": 0,
|
|
95
95
|
"extractionCount": 0,
|
|
96
|
-
"jobRunCount":
|
|
96
|
+
"jobRunCount": 0,
|
|
97
97
|
"refinementTaskCount": 0,
|
|
98
98
|
"usageCount": 0,
|
|
99
99
|
"candidateCount": 0,
|
|
100
|
-
"reportCount":
|
|
100
|
+
"reportCount": 0,
|
|
101
101
|
"scheduleCount": 3,
|
|
102
102
|
"note": "Structured knowledge uses SQL-backed sources, nodes, edges, issues, extractions, and job runs. Markdown is an optional projection, not the source of truth."
|
|
103
103
|
}
|
|
@@ -119,25 +119,25 @@ Doctor command completed without findings; provider/model identifiers and creden
|
|
|
119
119
|
### Authenticated connected-host /status
|
|
120
120
|
|
|
121
121
|
```text
|
|
122
|
-
{"status":"running"}
|
|
122
|
+
{"status":"running","version":"0.33.36"}
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
### Authenticated connected-host /api/health
|
|
126
126
|
|
|
127
127
|
```text
|
|
128
|
-
{"overall":"healthy","degradedDomains":[],"providerProblems":[],"mcpProblems":{"degraded":[],"quarantined":[]},"integrationProblems":[],"network":{"controlPlane":{"surface":"controlPlane","host":"
|
|
128
|
+
{"overall":"healthy","degradedDomains":[],"providerProblems":[],"mcpProblems":{"degraded":[],"quarantined":[]},"integrationProblems":[],"network":{"controlPlane":{"surface":"controlPlane","host":"127.0.0.1","port":3421,"mode":"off","scheme":"http","trustProxy":false,"usingDefaultPaths":false,"ready":true,"errors":[]},"httpListener":{"surface":"httpListener","host":"127.0.0.1","port":3422,"mode":"off","scheme":"http","trustProxy":false,"usingDefaultPaths":false,"ready":true,"errors":[]},"outbound":{"mode":"bundled","allowInsecureLocalhost":false,"customCaEntryCount":0,"effectiveCaStrategy":"bun-default","errors":[]}}}
|
|
129
129
|
```
|
|
130
130
|
|
|
131
131
|
### OpenAI-compatible /v1/models route
|
|
132
132
|
|
|
133
133
|
```text
|
|
134
|
-
/v1/models returned
|
|
134
|
+
/v1/models returned 175 model(s); model identifiers omitted from release artifact.
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
### Agent Knowledge isolated /status
|
|
138
138
|
|
|
139
139
|
```text
|
|
140
|
-
{"ready":true,"storagePath":"[goodvibes-home]/tui/knowledge-agent.sqlite","sourceCount":0,"nodeCount":
|
|
140
|
+
{"ready":true,"storagePath":"[goodvibes-home]/tui/knowledge-agent.sqlite","sourceCount":0,"nodeCount":0,"edgeCount":0,"issueCount":0,"extractionCount":0,"jobRunCount":0,"refinementTaskCount":0,"usageCount":0,"candidateCount":0,"reportCount":0,"scheduleCount":3,"note":"Structured knowledge uses SQL-backed sources, nodes, edges, issues, extractions, and job runs. Markdown is an optional projection, not the source of truth."}
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
### Agent Knowledge isolated ask
|
|
@@ -161,7 +161,7 @@ Doctor command completed without findings; provider/model identifiers and creden
|
|
|
161
161
|
### Agent Knowledge isolated nodes list
|
|
162
162
|
|
|
163
163
|
```text
|
|
164
|
-
{"nodes":[
|
|
164
|
+
{"nodes":[]}
|
|
165
165
|
```
|
|
166
166
|
|
|
167
167
|
### Agent Knowledge isolated issues list
|
|
@@ -173,7 +173,7 @@ Doctor command completed without findings; provider/model identifiers and creden
|
|
|
173
173
|
### Agent Knowledge isolated map
|
|
174
174
|
|
|
175
175
|
```text
|
|
176
|
-
{"ok":true,"title":"Knowledge Map","generatedAt":
|
|
176
|
+
{"ok":true,"title":"Knowledge Map","generatedAt":1781472844439,"width":1280,"height":920,"nodeCount":0,"edgeCount":0,"totalNodeCount":0,"totalEdgeCount":0,"facets":{"recordKinds":[],"nodeKinds":[],"sourceTypes":[],"sourceStatuses":[],"nodeStatuses":[],"issueCodes":[],"issueStatuses":[],"issueSeverities":[],"edgeRelations":[],"tags":[]},"nodes":[],"edges":[],"svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1280\" height=\"920\" viewBox=\"0 0 1280 920\" role=\"img\" aria-label=\"Knowledge Map\">\n<defs>\n <radialGradient id=\"knowledgeMapBg\" cx=\"50%\" cy=\"46%\" r=\"70%\">\n <stop offset=\"0%\" stop-color=\"#f7f4ec\" />\n <stop offset=\"60%\" stop-color=\"#e9eef0\" />\n <stop offset=\"100%\" stop-color=\"#dde6df\" />\n </radialGradient>\n <filter id=\"softShadow\" x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\">\n <feDropShadow dx=\"0\" dy=\"6\... [truncated]
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
### Agent Knowledge isolated connectors list
|
package/release/release-notes.md
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
- v1.5.
|
|
2
|
-
-
|
|
3
|
-
-
|
|
4
|
-
-
|
|
5
|
-
-
|
|
6
|
-
- Competitive parity wave: local calendar support, direct email send and read over SMTP and IMAP with TLS or STARTTLS, hardware-aware model recommendations, and automatic skill drafts.
|
|
7
|
-
- Skills now import and export in the open skill standard. Exports are lossless, and skills discovered from imports stay disabled until explicitly enabled.
|
|
8
|
-
- Hardened the email and calendar surfaces: SMTP and IMAP commands validate addresses and reject control characters, STARTTLS upgrades verify no data arrives before negotiation completes, and calendar parsing is stricter about malformed input.
|
|
9
|
-
- Reliability fixes across the TUI: closed a text-wrap hang, made wide-character rendering consistent, fixed webhook notifier reuse, routed the benchmark editor to its real action, and made command failures surface user-visible errors everywhere.
|
|
10
|
-
- Release and verification hygiene: the operator token is redacted from release artifacts, the coverage ledger reports honest numbers, release evidence is checked for existence, and operator-facing copy passes a plain-language gate.
|
|
11
|
-
- Test suite: 7931 pass / 0 fail / 2 skip across 554 files.
|
|
1
|
+
- v1.5.2 is a patch on the 1.5 line. The fullscreen Agent workspace, Agent-local behavior, isolated Agent Knowledge, connected-host operator integration, and explicit side-effect boundaries all stay in force; this release picks up the latest bundled platform SDK and keeps review scoring aligned with it.
|
|
2
|
+
- Updated the bundled GoodVibes platform SDK to 0.33.38, so Agent runs on the current shared review, agent, and reporting primitives.
|
|
3
|
+
- Tightened review pass/fail reporting to match the updated SDK: a review only passes when its score meets or exceeds the configured threshold. Prose like "passed" or "approved" no longer lifts a sub-threshold score over the bar, removing a path where a weak review could read as a pass.
|
|
4
|
+
- Explicit fail language in a review at or above the threshold is still honored as a safety override unless the same review also states it passed, so genuine failures are not silently swallowed.
|
|
5
|
+
- No change to your conversations, memory, skills, routines, or operator confirmation gates; this is an internal dependency and scoring-consistency update.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShellPassthrough — runs user-typed `!<command>` shell escapes.
|
|
3
|
+
*
|
|
4
|
+
* The composer routes a leading `!` to "shell" mode; this runs the command
|
|
5
|
+
* directly (a user-initiated terminal escape, not an agent tool call), shows
|
|
6
|
+
* its output, and buffers a context block that is prepended to the user's next
|
|
7
|
+
* real message — making the result visible to the model on the next turn
|
|
8
|
+
* without triggering a turn of its own.
|
|
9
|
+
*
|
|
10
|
+
* A buffer is used rather than appending a conversation message because the
|
|
11
|
+
* SDK drops `system` messages from the LLM view and does not merge consecutive
|
|
12
|
+
* `user` messages (which would trip a consecutive-user-message API error).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const MAX_SHELL_OUTPUT = 16_000;
|
|
16
|
+
|
|
17
|
+
export const SHELL_USAGE_HINT =
|
|
18
|
+
'[Shell] Usage: !<command> — runs a shell command; its output is shown and added as context for your next message.';
|
|
19
|
+
|
|
20
|
+
export interface ShellRunResult {
|
|
21
|
+
/** Formatted output for the conversation/activity feed. */
|
|
22
|
+
readonly display: string;
|
|
23
|
+
/** Context block buffered for the user's next turn. */
|
|
24
|
+
readonly context: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class ShellPassthrough {
|
|
28
|
+
private pending: string[] = [];
|
|
29
|
+
|
|
30
|
+
/** Run `command` in `cwd`, returning display + model-context renderings. */
|
|
31
|
+
async run(command: string, cwd: string): Promise<ShellRunResult> {
|
|
32
|
+
const proc = Bun.spawn(['bash', '-c', command], { cwd, stdout: 'pipe', stderr: 'pipe' });
|
|
33
|
+
const [out, err] = await Promise.all([
|
|
34
|
+
new Response(proc.stdout).text(),
|
|
35
|
+
new Response(proc.stderr).text(),
|
|
36
|
+
]);
|
|
37
|
+
const exitCode = await proc.exited;
|
|
38
|
+
const combined = [out, err].filter((s) => s.trim().length > 0).join('\n').trimEnd();
|
|
39
|
+
const truncated = combined.length > MAX_SHELL_OUTPUT
|
|
40
|
+
? `${combined.slice(0, MAX_SHELL_OUTPUT)}\n… [truncated ${combined.length - MAX_SHELL_OUTPUT} chars]`
|
|
41
|
+
: combined;
|
|
42
|
+
const body = truncated.length > 0 ? truncated : '(no output)';
|
|
43
|
+
const result: ShellRunResult = {
|
|
44
|
+
display: `[Shell] ${body}\n(exit ${exitCode})`,
|
|
45
|
+
context: `The user ran a shell command via the \`!\` prefix:\n$ ${command}\n--- output (exit ${exitCode}) ---\n${body}`,
|
|
46
|
+
};
|
|
47
|
+
this.pending.push(result.context);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Prepend (and clear) any buffered shell context onto an outgoing message. */
|
|
52
|
+
consumeContext(text: string): string {
|
|
53
|
+
if (this.pending.length === 0) return text;
|
|
54
|
+
const context = this.pending.join('\n\n');
|
|
55
|
+
this.pending = [];
|
|
56
|
+
return text ? `${context}\n\n${text}` : context;
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { renderSettingsModal } from './renderer/settings-modal.ts';
|
|
|
20
20
|
import { registerBuiltinCommands } from './input/commands.ts';
|
|
21
21
|
import { ScheduleManager } from '@pellux/goodvibes-sdk/platform/tools';
|
|
22
22
|
import { InputHistory } from './input/input-history.ts';
|
|
23
|
+
import { ShellPassthrough, SHELL_USAGE_HINT } from './input/shell-passthrough.ts';
|
|
23
24
|
import { getTierPromptSupplement, getTierForContextWindow } from '@pellux/goodvibes-sdk/platform/providers';
|
|
24
25
|
import { createShellLayout } from './renderer/layout-engine.ts';
|
|
25
26
|
import { buildShellFooter, estimateShellFooterHeight } from './renderer/shell-surface.ts';
|
|
@@ -156,6 +157,8 @@ async function main() {
|
|
|
156
157
|
let scrollTop = 0;
|
|
157
158
|
let scrollLocked = true;
|
|
158
159
|
|
|
160
|
+
const shellPassthrough = new ShellPassthrough();
|
|
161
|
+
|
|
159
162
|
// Ambient autonomy surfacing: away digest at launch + sidebar Coming up.
|
|
160
163
|
const autonomy = createAutonomySurfacing({
|
|
161
164
|
shellPaths: ctx.services.shellPaths,
|
|
@@ -315,14 +318,28 @@ async function main() {
|
|
|
315
318
|
systemMessageRouter.high(`[Memory] Pinned: "${memoryText}" (${memId})`);
|
|
316
319
|
processedText = memoryText;
|
|
317
320
|
}
|
|
321
|
+
} else if (processedText.startsWith('!')) {
|
|
322
|
+
const command = processedText.slice(1).trim();
|
|
323
|
+
if (!command) {
|
|
324
|
+
systemMessageRouter.high(SHELL_USAGE_HINT);
|
|
325
|
+
render();
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
systemMessageRouter.high(`[Shell] $ ${command}`); render();
|
|
329
|
+
void shellPassthrough.run(command, workingDir)
|
|
330
|
+
.then((result) => systemMessageRouter.high(result.display))
|
|
331
|
+
.catch((shellErr: unknown) => systemMessageRouter.high(`[Shell] Failed to run: ${summarizeError(shellErr)}`))
|
|
332
|
+
.finally(() => render());
|
|
333
|
+
return;
|
|
318
334
|
}
|
|
319
335
|
if (processedText || content) {
|
|
320
336
|
void (async () => {
|
|
321
337
|
const inputOptions = options.spokenOutput ? createSpokenTurnInputOptions() : undefined;
|
|
338
|
+
const outgoing = shellPassthrough.consumeContext(processedText);
|
|
322
339
|
if (options.spokenOutput && processedText) {
|
|
323
340
|
spokenTurns.submitNextTurn(processedText);
|
|
324
341
|
}
|
|
325
|
-
orchestrator.handleUserInput(
|
|
342
|
+
orchestrator.handleUserInput(outgoing, content, inputOptions).catch((err: unknown) => {
|
|
326
343
|
logger.debug('handleUserInput safety catch (already handled by runTurn)', { error: summarizeError(err) });
|
|
327
344
|
});
|
|
328
345
|
})();
|
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.5.
|
|
9
|
+
let _version = '1.5.2';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8')) as {
|
|
12
12
|
readonly version?: unknown;
|