dsh-ai-terminal-pet 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -36
- package/lib/index.js +97 -17
- package/package.json +1 -1
- package/vendor/AI-Terminal-Pet.exe +0 -0
- package/vendor/manifest.json +3 -3
- package/vendor/package.json +1 -0
package/README.md
CHANGED
|
@@ -19,42 +19,50 @@ maintainer pins a release in lib/release-pins.json, see below).
|
|
|
19
19
|
|
|
20
20
|
## What it does
|
|
21
21
|
|
|
22
|
-
| Harness
|
|
22
|
+
| Harness signal | Overlay state |
|
|
23
23
|
| --- | --- |
|
|
24
|
-
| turn/start
|
|
25
|
-
| tool/call | tool_use |
|
|
26
|
-
| approval/
|
|
27
|
-
| tool/result with error
|
|
28
|
-
| turn/end
|
|
24
|
+
| `turn/start`, `agent/inbox/claimed`, `agent/status` running | working |
|
|
25
|
+
| `tool/call` | tool_use |
|
|
26
|
+
| `approval/request` (waterfall listener) | waiting_permission |
|
|
27
|
+
| `tool/result` with `error`, `agent/error` | error |
|
|
28
|
+
| `turn/end`, `agent/status` idle | idle |
|
|
29
29
|
| Harness process exit | closed |
|
|
30
30
|
|
|
31
|
+
Events this table does not list are not part of the Harness session log: there is
|
|
32
|
+
no `command/done` or `working-directory/change` in `SessionEventMap`. The project
|
|
33
|
+
path, the parent session and the session start time do not come from events at
|
|
34
|
+
all - they are read from `session.header` (`cwd`, `parentSession`, `createdAt`),
|
|
35
|
+
which is the only place the host states them. That is also why a session shows
|
|
36
|
+
the right project even though the desktop Harness itself runs from its install
|
|
37
|
+
directory, and why a subagent conversation is nested under the session it was
|
|
38
|
+
forked from.
|
|
39
|
+
|
|
31
40
|
The overlay then plays an alert tone, shows a tray balloon on those transitions
|
|
32
41
|
and its one-click jump button targets the Harness window. Plugin reload (HMR)
|
|
33
42
|
disposes the listeners but deliberately does not close sessions: only a real
|
|
34
43
|
process exit means the conversation is over.
|
|
35
44
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
so read them as a description, not a permission grant.
|
|
45
|
+
## Where it is listed, and what each market indexes
|
|
46
|
+
|
|
47
|
+
Three independent paths exist today, and each one reads a different field:
|
|
48
|
+
|
|
49
|
+
| Market | What it indexes | What this package offers it |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| npm registry | package name, version, `dist.integrity` | published as `dsh-ai-terminal-pet`; a market that installs by name gets a verified tarball |
|
|
52
|
+
| dshmarket (the market tab most Harness Desktop users run) | `https://awesome-dsh-plugin.com/plugins.json`, which is generated from the [awesome-dsh-plugin](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin) repository | entry merged via PR [#13937](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin/pull/13937); the `install` command it renders comes from that entry's pinned `tarball`, so bumping it is always a pull request *there*, never an edit here |
|
|
53
|
+
| GitHub-harvesting markets | repositories carrying the `dsh-plugin` topic plus a root `package.json` declaring `name` and `dsh.bundle` | this repository has the topic and the root shim manifest, which is what makes `dsh plugin add github:owner/repo` resolve instead of mounting a 0.0.0 dependency |
|
|
54
|
+
|
|
55
|
+
"Listed" and "visible" are different states, and only the second one helps a
|
|
56
|
+
user: the awesome entry existed before its `plugins.json` entry did, because the
|
|
57
|
+
site builds the catalog on a schedule. Check the market's own public index, not
|
|
58
|
+
the merged pull request. The package README cannot promise what another
|
|
59
|
+
repository's build will render; it can only promise that the fields those builds
|
|
60
|
+
read are present and correct.
|
|
61
|
+
|
|
62
|
+
The package describes itself to storefronts through `dsh.catalog`: a category, a
|
|
63
|
+
one-line summary in both languages, and the capabilities it uses. Capabilities
|
|
64
|
+
are self-declared and never checked — DSH does not sandbox plugins — so read them
|
|
65
|
+
as a description, not a permission grant.
|
|
58
66
|
The bundle is Windows only and declares `dsh.bundle.platforms: ["win32"]`, so
|
|
59
67
|
other platforms show it as unsupported instead of installing it.
|
|
60
68
|
|
|
@@ -64,17 +72,26 @@ that field - the bundle mounts through `dsh.bundle` either way - but storefronts
|
|
|
64
72
|
that sort entries by profile do: without it a registry falls back to a
|
|
65
73
|
headless-or-web guess and a desktop client hides the entry.
|
|
66
74
|
|
|
67
|
-
Manually, from a terminal, into a profile you own —
|
|
68
|
-
|
|
75
|
+
Manually, from a terminal, into a profile you own — by package name, by release
|
|
76
|
+
tarball, or by pinning the release tag. All three are verified end to end:
|
|
69
77
|
|
|
70
78
|
```sh
|
|
71
|
-
#
|
|
72
|
-
dsh plugin --profile <profile> add
|
|
79
|
+
# by package name: published on npm, no clone, no Go toolchain
|
|
80
|
+
dsh plugin --profile <profile> add dsh-ai-terminal-pet
|
|
81
|
+
|
|
82
|
+
# release tarball: same bytes as the npm release, straight from the GitHub Release
|
|
83
|
+
dsh plugin --profile <profile> add https://github.com/jjjjjjjjnnjnn/ai-terminal-pet/releases/download/dsh-plugin-v1.2.6/dsh-ai-terminal-pet-1.2.6.tgz
|
|
73
84
|
|
|
74
85
|
# git source, pinned to the release tag
|
|
75
|
-
dsh plugin --profile <profile> add "git+https://github.com/jjjjjjjjnnjnn/ai-terminal-pet.git#dsh-plugin-v1.2.
|
|
86
|
+
dsh plugin --profile <profile> add "git+https://github.com/jjjjjjjjnnjnn/ai-terminal-pet.git#dsh-plugin-v1.2.6&path:/packages/dsh-ai-terminal-pet"
|
|
76
87
|
```
|
|
77
88
|
|
|
89
|
+
Those three differ only in transport. The npm path is the shortest and the one a
|
|
90
|
+
market uses when it installs by name; the tarball path works when a market pins a
|
|
91
|
+
release asset instead of a registry entry; the git path is the fallback for a
|
|
92
|
+
machine that cannot reach npm at all. Every one of them lands the same
|
|
93
|
+
prebuilt overlay in `vendor/`.
|
|
94
|
+
|
|
78
95
|
Do not hand pnpm a bare repository URL. pnpm resolves a git dependency by
|
|
79
96
|
running the package's lifecycle scripts, and it blocks those for unpinned
|
|
80
97
|
git sources until you copy the `allowBuilds` key it prints into your profile's
|
|
@@ -85,9 +102,12 @@ SHA-256 of that file, the SHA-256 of the Go sources it was built from, and the
|
|
|
85
102
|
Go version that produced it, so a stale or hand-edited vendor directory fails a
|
|
86
103
|
check instead of shipping quietly.
|
|
87
104
|
|
|
88
|
-
The GitHub paths above need
|
|
89
|
-
|
|
90
|
-
|
|
105
|
+
The GitHub and npm paths above need nothing from this repository at install time.
|
|
106
|
+
`dsh plugin add dsh-ai-terminal-pet` resolves through the npm registry; the
|
|
107
|
+
tarball and git forms resolve through GitHub. A marketplace that indexes npm
|
|
108
|
+
sees the package under its own name and can verify the published integrity
|
|
109
|
+
digest; a marketplace that indexes repositories sees the `dsh-plugin` topic and
|
|
110
|
+
the root shim manifest.
|
|
91
111
|
|
|
92
112
|
`add` is the whole install: the profile records the dependency, and the bundle
|
|
93
113
|
declares itself through `dsh.bundle.patch`, so no separate `enable` step exists.
|
package/lib/index.js
CHANGED
|
@@ -125,20 +125,51 @@ function officialProtocol() {
|
|
|
125
125
|
/** Per-conversation state. Kept at module scope so plugin HMR does not lose it. */
|
|
126
126
|
const sessions = new Map()
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
/**
|
|
129
|
+
* The session header, whichever shape the caller was handed.
|
|
130
|
+
*
|
|
131
|
+
* `session/event` hands over the session object itself; other events hand over
|
|
132
|
+
* a wrapper with `.agent`. The header is the only place the Harness states a
|
|
133
|
+
* session's cwd, lineage and creation time, so everything below reads it from
|
|
134
|
+
* here instead of guessing.
|
|
135
|
+
*/
|
|
136
|
+
function headerOf(candidate) {
|
|
137
|
+
return candidate?.session?.header ?? candidate?.header ?? candidate?.agent?.session?.header ?? null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** What the header says about one candidate, with safe fallbacks. */
|
|
141
|
+
function sessionInfo(candidate) {
|
|
142
|
+
const header = headerOf(candidate)
|
|
143
|
+
const cwd = header && typeof header.cwd === 'string' ? header.cwd : ''
|
|
144
|
+
const parent = header && header.parentSession != null ? String(header.parentSession) : ''
|
|
145
|
+
const createdAt = header && Number.isFinite(header.createdAt) ? header.createdAt : 0
|
|
146
|
+
return { id: sessionIdOf(candidate), cwd, parent, createdAt }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function metaFor(sessionId, info) {
|
|
129
150
|
let item = sessions.get(sessionId)
|
|
130
151
|
if (!item) {
|
|
131
152
|
item = {
|
|
132
153
|
state: 'idle',
|
|
133
|
-
|
|
154
|
+
// The session's own cwd, never the host process's: a desktop Harness runs
|
|
155
|
+
// from its install directory, so process.cwd() would label every session
|
|
156
|
+
// with the same wrong path.
|
|
157
|
+
project: info?.cwd || safeCwd(),
|
|
134
158
|
title: TITLE,
|
|
135
159
|
tool: '',
|
|
136
160
|
reason: '',
|
|
137
|
-
|
|
161
|
+
parentSessionId: info?.parent || '',
|
|
162
|
+
startedAt: new Date(info?.createdAt || Date.now()).toISOString(),
|
|
138
163
|
stateAt: 0,
|
|
139
164
|
sentAt: 0,
|
|
140
165
|
}
|
|
141
166
|
sessions.set(sessionId, item)
|
|
167
|
+
} else if (info) {
|
|
168
|
+
// The header is authoritative for a session that has not stated a project
|
|
169
|
+
// yet. An explicit project from an event wins for that report, which is
|
|
170
|
+
// what keeps a future cwd-change extension working.
|
|
171
|
+
if (!item.project && info.cwd) item.project = info.cwd
|
|
172
|
+
if (info.parent) item.parentSessionId = info.parent
|
|
142
173
|
}
|
|
143
174
|
return item
|
|
144
175
|
}
|
|
@@ -166,25 +197,47 @@ function sessionIdOf(...candidates) {
|
|
|
166
197
|
return 'dsh-main'
|
|
167
198
|
}
|
|
168
199
|
|
|
200
|
+
/**
|
|
201
|
+
* What makes one report different from the previous one.
|
|
202
|
+
*
|
|
203
|
+
* The state alone is not enough: a patch can move the project, the reason, the
|
|
204
|
+
* tool or the lineage without touching the state, and the panel shows all of
|
|
205
|
+
* them. De-duplicating on state alone silently dropped those updates. Identical
|
|
206
|
+
* repeats are still dropped, which is what keeps a tool that fires fifty events
|
|
207
|
+
* per second from flooding the pipe.
|
|
208
|
+
*/
|
|
209
|
+
function signatureOf(item) {
|
|
210
|
+
return [
|
|
211
|
+
item.state,
|
|
212
|
+
item.reason || '',
|
|
213
|
+
item.tool || '',
|
|
214
|
+
item.project || '',
|
|
215
|
+
item.title || '',
|
|
216
|
+
item.parentSessionId || '',
|
|
217
|
+
].join('\u0000')
|
|
218
|
+
}
|
|
219
|
+
|
|
169
220
|
/**
|
|
170
221
|
* Publish one status. The protocol module writes the durable snapshot
|
|
171
222
|
* synchronously before its first await, so calling this from process.on('exit')
|
|
172
223
|
* still leaves a correct closed-state snapshot even when the pipe is gone.
|
|
173
224
|
*/
|
|
174
|
-
function report(sessionId, patch, force = false) {
|
|
225
|
+
function report(sessionId, patch, force = false, info = null) {
|
|
175
226
|
const p = officialProtocol()
|
|
176
227
|
if (!p || typeof p.sendStatus !== 'function') return
|
|
177
|
-
const item = metaFor(sessionId)
|
|
178
|
-
const
|
|
228
|
+
const item = metaFor(sessionId, info)
|
|
229
|
+
const previousState = item.state
|
|
230
|
+
const previousSignature = signatureOf(item)
|
|
179
231
|
Object.assign(item, patch)
|
|
180
232
|
if (!p.STATES.has(item.state)) item.state = 'idle'
|
|
181
233
|
|
|
182
234
|
const now = Date.now()
|
|
183
|
-
const changed = item
|
|
235
|
+
const changed = signatureOf(item) !== previousSignature
|
|
236
|
+
const stateChanged = item.state !== previousState
|
|
184
237
|
const stale = now - item.sentAt >= KEEP_ALIVE_MS
|
|
185
238
|
if (!force && !changed && !stale) return
|
|
186
239
|
item.sentAt = now
|
|
187
|
-
if (
|
|
240
|
+
if (stateChanged) item.stateAt = now
|
|
188
241
|
|
|
189
242
|
const stablePid = typeof p.stableProcessId === 'function'
|
|
190
243
|
? p.stableProcessId(SOURCE + ':' + sessionId)
|
|
@@ -194,7 +247,7 @@ function report(sessionId, patch, force = false) {
|
|
|
194
247
|
schemaVersion: p.SCHEMA_VERSION,
|
|
195
248
|
source: SOURCE,
|
|
196
249
|
sessionId,
|
|
197
|
-
parentSessionId: '',
|
|
250
|
+
parentSessionId: item.parentSessionId || '',
|
|
198
251
|
// Synthetic per-conversation pid (never used for OS liveness) plus the
|
|
199
252
|
// real Harness host pid as the ancestor-chain anchor.
|
|
200
253
|
pid: stablePid,
|
|
@@ -219,6 +272,17 @@ function report(sessionId, patch, force = false) {
|
|
|
219
272
|
}
|
|
220
273
|
}
|
|
221
274
|
|
|
275
|
+
/**
|
|
276
|
+
* Report for whatever object an event handler was given, after folding in what
|
|
277
|
+
* its session header says. Every handler goes through here so the project path
|
|
278
|
+
* and the parent link are never taken from the host process or invented per
|
|
279
|
+
* event type.
|
|
280
|
+
*/
|
|
281
|
+
function reportFor(candidate, patch, force = false) {
|
|
282
|
+
const info = sessionInfo(candidate)
|
|
283
|
+
return report(info.id, patch, force, info)
|
|
284
|
+
}
|
|
285
|
+
|
|
222
286
|
/** Copy one file into place through a temp sibling, never replacing a file. */
|
|
223
287
|
function copyVerified(source, destination, mode) {
|
|
224
288
|
let temp = ''
|
|
@@ -436,7 +500,24 @@ function bootstrap(ctx) {
|
|
|
436
500
|
return bootstrapPromise
|
|
437
501
|
}
|
|
438
502
|
|
|
439
|
-
/**
|
|
503
|
+
/**
|
|
504
|
+
* Map one durable session event onto a state patch.
|
|
505
|
+
*
|
|
506
|
+
* The authoritative list lives in @deepseek-ai/dsh-session's SessionEventMap
|
|
507
|
+
* (`turn/start`, `turn/end`, `step/start`, `step/end`, `user/message`,
|
|
508
|
+
* `assistant/chunk`, `assistant/message`, `tool/call`, `tool/result`,
|
|
509
|
+
* `todo/write`, `request/header`, `request/context`, `session/end-seed`), which
|
|
510
|
+
* this switch covers for every event that says something about activity.
|
|
511
|
+
*
|
|
512
|
+
* `working-directory/change` and `session/title` are NOT in that map: the first
|
|
513
|
+
* does not exist anywhere in the host, the second is a client-side event. Both
|
|
514
|
+
* branches are kept as cheap forward-compatibility - if a future version emits
|
|
515
|
+
* them, they map correctly - but the project path and the lineage come from
|
|
516
|
+
* session.header (see reportFor), which is the only place the host states them.
|
|
517
|
+
* `approval/asked` / `approval/decided` likewise come from the approval service
|
|
518
|
+
* rather than the session log; the approval/request waterfall listener in
|
|
519
|
+
* apply() is the path that actually fires.
|
|
520
|
+
*/
|
|
440
521
|
function eventPatch(type, data) {
|
|
441
522
|
switch (type) {
|
|
442
523
|
case 'approval/asked':
|
|
@@ -496,26 +577,25 @@ export function apply(ctx, config) {
|
|
|
496
577
|
}
|
|
497
578
|
|
|
498
579
|
on('session/event', (session, event) => {
|
|
499
|
-
const sessionId = sessionIdOf(session)
|
|
500
580
|
const patch = eventPatch(event?.type, event?.data)
|
|
501
|
-
if (patch)
|
|
581
|
+
if (patch) reportFor(session, patch)
|
|
502
582
|
})
|
|
503
583
|
|
|
504
584
|
on('agent/inbox/claimed', (payload) => {
|
|
505
|
-
|
|
585
|
+
reportFor(payload?.agent, { state: 'working', reason: '', tool: '' })
|
|
506
586
|
})
|
|
507
587
|
|
|
508
588
|
on('agent/status', (payload) => {
|
|
509
589
|
const status = String(payload?.status ?? '').toLowerCase()
|
|
510
590
|
if (/run|work|busy|think|stream/.test(status)) {
|
|
511
|
-
|
|
591
|
+
reportFor(payload?.agent, { state: 'working' })
|
|
512
592
|
} else if (/idle|done|wait|stop/.test(status)) {
|
|
513
|
-
|
|
593
|
+
reportFor(payload?.agent, { state: 'idle' })
|
|
514
594
|
}
|
|
515
595
|
})
|
|
516
596
|
|
|
517
597
|
on('agent/error', (payload) => {
|
|
518
|
-
|
|
598
|
+
reportFor(payload?.agent, {
|
|
519
599
|
state: 'error',
|
|
520
600
|
reason: short(payload?.error?.message ?? payload?.error, 120),
|
|
521
601
|
})
|
|
@@ -524,7 +604,7 @@ export function apply(ctx, config) {
|
|
|
524
604
|
// approval/request is a waterfall listener: it must always call next(),
|
|
525
605
|
// otherwise other plugins' approval handling is silently dropped.
|
|
526
606
|
on('approval/request', (request, next) => {
|
|
527
|
-
|
|
607
|
+
reportFor(request?.agent, {
|
|
528
608
|
state: 'waiting_permission',
|
|
529
609
|
reason: 'permission required',
|
|
530
610
|
tool: short(request?.toolName, 120),
|
package/package.json
CHANGED
|
Binary file
|
package/vendor/manifest.json
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
"generatedBy": "scripts/vendor.mjs",
|
|
4
4
|
"goVersion": "go1.27.0",
|
|
5
5
|
"source": {
|
|
6
|
-
"files":
|
|
7
|
-
"sha256": "
|
|
6
|
+
"files": 24,
|
|
7
|
+
"sha256": "6e2a4511fa4a77c15160a3cb861d77eaff30982d9a6dcafb0b11cb025a5dd454"
|
|
8
8
|
},
|
|
9
9
|
"overlay": {
|
|
10
10
|
"file": "vendor/AI-Terminal-Pet.exe",
|
|
11
11
|
"bytes": 4371968,
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "cd9011ddf98ebb051d2ccd568913bba89525e6d067e9306bb22a85a8951504ef"
|
|
13
13
|
},
|
|
14
14
|
"protocol": {
|
|
15
15
|
"file": "vendor/protocol.js",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{ "type": "commonjs" }
|