dsh-working-activity 0.1.0
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.i18n.yaml +6 -0
- package/README.md +109 -0
- package/README.zh.md +111 -0
- package/lib/types/events.d.ts +42 -0
- package/lib/types/events.d.ts.map +1 -0
- package/lib/types/events.js +9 -0
- package/lib/types/index.d.ts +61 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +151 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/invariant.d.ts.map +1 -0
- package/lib/types/invariant.js +61 -0
- package/lib/types/phrases.d.ts +55 -0
- package/lib/types/phrases.d.ts.map +1 -0
- package/lib/types/phrases.js +163 -0
- package/lib/types/status.d.ts +110 -0
- package/lib/types/status.d.ts.map +1 -0
- package/lib/types/status.js +440 -0
- package/package.json +61 -0
- package/src/events.ts +45 -0
- package/src/index.ts +211 -0
- package/src/invariant.ts +69 -0
- package/src/phrases.ts +180 -0
- package/src/status.ts +501 -0
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/activity/working-activity/README.md
|
|
5
|
+
README.md: bf0787caac11a292882ba1a35963878e373e7db8
|
|
6
|
+
README.zh.md: de09a7945dbe42c85d359fd256cdac0264d4b6cd
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# dsh-working-activity
|
|
2
|
+
|
|
3
|
+
A live "working line" for DeepSeek Harness: the model's real-time activity — playful thinking copy, the tool actually running, elapsed time, and a turn-end summary — shown while the agent works.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Folds the durable session stream (`turn/start`, `assistant/chunk`, `tool/call`, `tool/result`, `turn/end`) plus `agent/status` into one status line, refreshed on a render tick:
|
|
8
|
+
|
|
9
|
+
- **Thinking**: short colloquial copy rotates every few seconds (`嗯…让我捋捋`, `盘一下盘一下`, `大脑转起来了`, deadpan `lol` / `hm` / `ok`), tiered when thinking runs long (30s / 1min / 5min), night-owl copy mixed in between 00:00–06:00 local time.
|
|
10
|
+
- **Tool activity**: the running tool renders as `俏皮动词 + 参数细节 + 已耗时` (`跑个命令 npm test · 12s`); failed tools show `翻车了`-style copy in the done line.
|
|
11
|
+
- **Turn summary**: on `turn/end` the line becomes `搞定 ✓ · N 工具 · 想Xs 干Ys` (thinking/tool split), with the last tool's fragment pinned for a few seconds.
|
|
12
|
+
- **Minimal mode**: `phrases: false` renders plain functional labels (`思考中 · 总1m23s`, `bash npm test · 12s`).
|
|
13
|
+
|
|
14
|
+
Two optional sinks, both off by default only when their seam is absent:
|
|
15
|
+
|
|
16
|
+
1. **TUI prompt slot** — registers the `${activity}` template value on `ctx.tuiPrompt` when the TUI is composed. Add `${activity}` to `theme.leftPrompt` to see it next to `cwd`/`model`/`context`.
|
|
17
|
+
2. **Session events** — appends log-only `activity/status` events (never surface events: the model never sees them) for Web and other UI consumers; replay ignores them.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
# cordis.yml
|
|
23
|
+
plugins:
|
|
24
|
+
- id: working-activity
|
|
25
|
+
name: 'dsh-working-activity'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## TUI usage
|
|
29
|
+
|
|
30
|
+
Enable the plugin alongside `dsh-tui` and add the slot to the left prompt template:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
plugins:
|
|
34
|
+
- id: tui
|
|
35
|
+
name: '@deepseek-ai/dsh-tui'
|
|
36
|
+
config:
|
|
37
|
+
theme:
|
|
38
|
+
leftPrompt: '${cwd}${git/worktree}${activity}${model}${token_meter/cache_hit_rate}${context}'
|
|
39
|
+
- id: working-activity
|
|
40
|
+
name: 'dsh-working-activity'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
While a turn runs, the prompt line shows e.g. `dsh main 跑个命令 npm test · 12s deepseek-chat …`; while thinking, `嗯…让我捋捋 · 总1m23s`; after the turn, `搞定 ✓ · 4 工具 · 想12s 干11s` briefly. Without `${activity}` in the template, the plugin is inert in the TUI (the slot is unregistered values are omitted by the template renderer).
|
|
44
|
+
|
|
45
|
+
## Web usage
|
|
46
|
+
|
|
47
|
+
The Web client renders the live line two ways, both fed by the `activity/status` events:
|
|
48
|
+
|
|
49
|
+
- The turn-level status label (`TurnStatus`, formerly the static "Deep diving...") shows the live line while a turn runs, keeping its sweep animation.
|
|
50
|
+
- A working-line dock entry (`WorkingLine` on `conversation.input.dock`) renders the turn-end statistics (token/elapsed/tool summary) above the composer after a turn settles; live phases stay on the turn label.
|
|
51
|
+
|
|
52
|
+
Both fall back to the previous static label while the plugin is absent, so the Web UI is unaffected by disabling it.
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
| Key | Type | Default | Meaning |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `phrases` | `boolean` | `true` | Playful copy pool; `false` renders plain functional labels |
|
|
59
|
+
| `publish` | `boolean` | `true` | Append `activity/status` session events for UI consumers |
|
|
60
|
+
| `tickMs` | `number` | `500` | Status render tick interval (50–5000) |
|
|
61
|
+
| `publishIntervalMs` | `number` | `2000` | Minimum interval between published events while the line is stable (500–30000) |
|
|
62
|
+
| `detailLimit` | `number` | `40` | Max displayed detail length (paths/commands/patterns), 8–120 |
|
|
63
|
+
| `customActions` | `object` | `{}` | Exact tool-name → action-copy pools, e.g. `{"my_deploy": ["部署一下", "上线中"]}` |
|
|
64
|
+
| `narrate` | `boolean` | `true` | Inject the `⏵` self-narration contract into the system prompt; the line is surfaced live and stripped from the chat body |
|
|
65
|
+
|
|
66
|
+
## Event contract
|
|
67
|
+
|
|
68
|
+
`activity/status` is a log-only session event (merge-extensible `SessionEventMap` member, no `surfaceOp`):
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
{
|
|
72
|
+
phase: 'idle' | 'waiting' | 'thinking' | 'tool' | 'done'
|
|
73
|
+
line: string // plain-text status line, no ANSI
|
|
74
|
+
label?: string // current work label (tool action / stage)
|
|
75
|
+
detail?: string // path / command / pattern fragment
|
|
76
|
+
phrase?: string // current playful phrase
|
|
77
|
+
toolCount: number // tools completed this turn
|
|
78
|
+
turnElapsedMs: number // ms since turn start
|
|
79
|
+
phaseStartedAt: number // epoch ms the phase started (animation anchor)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Publishing rules: line changes publish immediately; a stable line republishes at most every `publishIntervalMs` so a long tool's elapsed time stays live without flooding the log. All data is lossless JSON; optional fields are omitted when absent.
|
|
84
|
+
|
|
85
|
+
## Export shape
|
|
86
|
+
|
|
87
|
+
A function/namespace plugin: `name` / `Config` / `apply`, no default export. The state machine (`ActivityTracker`) and copy pools live in `./status` and `./phrases` (pure, clock-injected, unit-tested). The invariant companion registers under `./invariant`.
|
|
88
|
+
|
|
89
|
+
## Model Experience
|
|
90
|
+
|
|
91
|
+
### Prompt and tool surface
|
|
92
|
+
|
|
93
|
+
Nothing. The plugin injects no prompt sections, registers no tools, and appends no surface events. `activity/status` is UI state only: it never enters derived model history, so the model cannot see its own working line.
|
|
94
|
+
|
|
95
|
+
### Token effect
|
|
96
|
+
|
|
97
|
+
Zero per request.
|
|
98
|
+
|
|
99
|
+
### KV Cache effect
|
|
100
|
+
|
|
101
|
+
No system-prompt contribution, so no cache-stability effect.
|
|
102
|
+
|
|
103
|
+
## Known Limitations and Deferred Work
|
|
104
|
+
|
|
105
|
+
- **Single active line**: the plugin keeps one status line per session; the TUI slot shows the most recently active session.
|
|
106
|
+
- **Narration is opt-in**: the `⏵` self-narration contract (model writes a short status line at the top of each reply) is injected by default (`narrate: true`); set `narrate: false` for a purely event-derived line.
|
|
107
|
+
- **No progress percentages**: DSH has no tool progress events; a long tool shows elapsed time only.
|
|
108
|
+
- **No animated frames**: the TUI slot renders a static text fragment; frame animation (moon/comet/braille presets) is deferred until the prompt-slot contract supports a frame callback.
|
|
109
|
+
- **Web dock entry duplicates the turn label**: the input-dock `WorkingLine` and the chat `TurnStatus` show the same snapshot; the dock entry exists for session views where the turn label is not visible.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# @deepseek-ai/dsh-working-activity
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+
为 DeepSeek Harness 打造的一条实时 "工作状态行":模型的实时活动——俏皮思考文案、真正在跑的工具、已耗时、收尾摘要——在 agent 干活时展示出来。
|
|
6
|
+
|
|
7
|
+
## 功能
|
|
8
|
+
|
|
9
|
+
把持久会话流(`turn/start`、`assistant/chunk`、`tool/call`、`tool/result`、`turn/end`)和 `agent/status` 折叠成一条状态行,按渲染 tick 刷新:
|
|
10
|
+
|
|
11
|
+
- **思考中**:每隔几秒轮换一句短俏皮文案(`嗯…让我捋捋`、`盘一下盘一下`、`大脑转起来了`,穿插面无表情的 `lol` / `hm` / `ok`);想久了自动分档(30 秒 / 1 分钟 / 5 分钟);本地时间 00:00–06:00 混入深夜专属文案。
|
|
12
|
+
- **工具活动**:正在运行的工具渲染为 `俏皮动词 + 参数细节 + 已耗时`(`跑个命令 npm test · 12s`);失败工具在收尾行显示 `翻车了` 风格文案。
|
|
13
|
+
- **收尾摘要**:`turn/end` 后状态行变为 `搞定 ✓ · N 工具 · 想Xs 干Ys`(思考/干活耗时拆分),并短暂钉住最后一个工具的片段。
|
|
14
|
+
- **极简模式**:`phrases: false` 时渲染朴素功能标签(`思考中 · 总1m23s`、`bash npm test · 12s`)。
|
|
15
|
+
|
|
16
|
+
两个可选出口,只有对应接缝存在时才会生效:
|
|
17
|
+
|
|
18
|
+
1. **TUI prompt 槽位** —— TUI 组合存在时,在 `ctx.tuiPrompt` 上注册 `${activity}` 模板值。把 `${activity}` 加进 `theme.leftPrompt` 即可在 `cwd`/`model`/`context` 旁边看到它。
|
|
19
|
+
2. **会话事件** —— 追加只记日志的 `activity/status` 事件(绝不是 surface 事件:模型永远看不到),供 Web 与其他 UI 消费;回放时忽略它们。
|
|
20
|
+
|
|
21
|
+
## 安装
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
# cordis.yml
|
|
25
|
+
plugins:
|
|
26
|
+
- id: working-activity
|
|
27
|
+
name: '@deepseek-ai/dsh-working-activity'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## TUI 用法
|
|
31
|
+
|
|
32
|
+
与 `dsh-tui` 一起启用插件,并把槽位加进左侧 prompt 模板:
|
|
33
|
+
|
|
34
|
+
```yaml
|
|
35
|
+
plugins:
|
|
36
|
+
- id: tui
|
|
37
|
+
name: '@deepseek-ai/dsh-tui'
|
|
38
|
+
config:
|
|
39
|
+
theme:
|
|
40
|
+
leftPrompt: '${cwd}${git/worktree}${activity}${model}${token_meter/cache_hit_rate}${context}'
|
|
41
|
+
- id: working-activity
|
|
42
|
+
name: '@deepseek-ai/dsh-working-activity'
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
一轮进行中时,提示行显示例如 `dsh main 跑个命令 npm test · 12s deepseek-chat …`;思考时显示 `嗯…让我捋捋 · 总1m23s`;收尾后短暂显示 `搞定 ✓ · 4 工具 · 想12s 干11s`。模板里没有 `${activity}` 时,插件在 TUI 中不产生任何效果(槽位未注册的值会被模板渲染器省略)。
|
|
46
|
+
|
|
47
|
+
## Web 用法
|
|
48
|
+
|
|
49
|
+
Web 客户端通过 `activity/status` 事件以两种方式渲染实时状态行:
|
|
50
|
+
|
|
51
|
+
- 回合级状态标签(`TurnStatus`,原先是静态的 "Deep diving..."):回合进行中显示实时状态行,保留原有的流光扫过特效。
|
|
52
|
+
- 输入区上方的状态行条目(`WorkingLine`,挂在 `conversation.input.dock`):回合结束后渲染收工统计(token/耗时/工具摘要);活动阶段只在轮辑标签上显示。
|
|
53
|
+
|
|
54
|
+
插件缺席时两者都回退到原来的静态标签,禁用插件不影响 Web UI。
|
|
55
|
+
|
|
56
|
+
## 配置
|
|
57
|
+
|
|
58
|
+
| 键 | 类型 | 默认值 | 含义 |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| `phrases` | `boolean` | `true` | 俏皮文案池;`false` 渲染朴素功能标签 |
|
|
61
|
+
| `publish` | `boolean` | `true` | 为 UI 消费者追加 `activity/status` 会话事件 |
|
|
62
|
+
| `tickMs` | `number` | `500` | 状态渲染 tick 间隔(50–5000) |
|
|
63
|
+
| `publishIntervalMs` | `number` | `2000` | 状态行稳定时两次发布事件的最小间隔(500–30000) |
|
|
64
|
+
| `detailLimit` | `number` | `40` | 详情最大展示长度(路径/命令/模式),8–120 |
|
|
65
|
+
| `customActions` | `object` | `{}` | 工具名精确匹配 → 动作文案池,如 `{"my_deploy": ["部署一下", "上线中"]}` |
|
|
66
|
+
| `narrate` | `boolean` | `true` | 向 system prompt 注入 `⏵` 自述约定;该行实时展示并从聊天正文中过滤 |
|
|
67
|
+
|
|
68
|
+
## 事件契约
|
|
69
|
+
|
|
70
|
+
`activity/status` 是只记日志的会话事件(merge 可扩展的 `SessionEventMap` 成员,无 `surfaceOp`):
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
{
|
|
74
|
+
phase: 'idle' | 'waiting' | 'thinking' | 'tool' | 'done'
|
|
75
|
+
line: string // 纯文本状态行,无 ANSI
|
|
76
|
+
label?: string // 当前工作标签(工具动作/阶段)
|
|
77
|
+
detail?: string // 路径 / 命令 / 模式片段
|
|
78
|
+
phrase?: string // 当前俏皮文案
|
|
79
|
+
toolCount: number // 本轮已完成的工具数
|
|
80
|
+
turnElapsedMs: number // 距本轮开始毫秒数
|
|
81
|
+
phaseStartedAt: number // 相位开始的 epoch 毫秒(动画锚点)
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
发布规则:状态行变化立即发布;稳定行最多每 `publishIntervalMs` 重发一次,让长工具的已耗时保持实时而不刷爆日志。所有数据都是无损 JSON;可选字段缺省时省略。
|
|
86
|
+
|
|
87
|
+
## 导出形状
|
|
88
|
+
|
|
89
|
+
函数/命名空间插件:`name` / `Config` / `apply`,无默认导出。状态机(`ActivityTracker`)与文案池在 `./status` 和 `./phrases`(纯逻辑、时钟注入、有单测)。不变量伴生插件在 `./invariant` 注册。
|
|
90
|
+
|
|
91
|
+
## Model Experience
|
|
92
|
+
|
|
93
|
+
### 提示词与工具面
|
|
94
|
+
|
|
95
|
+
没有。插件不注入任何提示词段、不注册任何工具、不追加任何 surface 事件。`activity/status` 只是 UI 状态:永不进入派生模型历史,模型看不到自己的工作状态行。
|
|
96
|
+
|
|
97
|
+
### Token 影响
|
|
98
|
+
|
|
99
|
+
每次请求为零。
|
|
100
|
+
|
|
101
|
+
### KV 缓存影响
|
|
102
|
+
|
|
103
|
+
不贡献 system prompt,因此无缓存稳定性影响。
|
|
104
|
+
|
|
105
|
+
## Known Limitations and Deferred Work
|
|
106
|
+
|
|
107
|
+
- **单条活跃状态行**:插件按会话维护一条状态行;TUI 槽位显示最近活跃会话。
|
|
108
|
+
- **自述可选**:`⏵` 模型自述约定(每次回复顶部写一行短状态文案)默认注入(`narrate: true`);设 `narrate: false` 则只由事件推导。
|
|
109
|
+
- **无进度百分比**:DSH 没有工具进度事件;长工具只显示已耗时。
|
|
110
|
+
- **无动画帧**:TUI 槽位渲染静态文本片段;帧动画(moon/comet/braille 预设)要等 prompt 槽位契约支持帧回调后再做。
|
|
111
|
+
- **Web 双入口重复显示**:输入区 `WorkingLine` 与聊天区 `TurnStatus` 显示同一快照;dock 条目用于回合标签不可见的会话视图。
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `activity/status` session event — a log-only, non-surface snapshot of the
|
|
3
|
+
* model's current working activity, published by this plugin for any UI
|
|
4
|
+
* consumer (Web client, telemetry, …). It never enters derived model history
|
|
5
|
+
* (no `surfaceOp`), so it cannot leak into prompts; UIs render it like
|
|
6
|
+
* `todo/write` or `plan/mode`.
|
|
7
|
+
* @module @deepseek-ai/dsh-working-activity/events
|
|
8
|
+
*/
|
|
9
|
+
import type { ActivityPhase } from './status.js';
|
|
10
|
+
/** Durable payload of one `activity/status` snapshot. */
|
|
11
|
+
export interface ActivityStatusEvent {
|
|
12
|
+
/** Which activity phase the model is in. */
|
|
13
|
+
readonly phase: ActivityPhase;
|
|
14
|
+
/** Human-readable status line (plain text, no ANSI). */
|
|
15
|
+
readonly line: string;
|
|
16
|
+
/** Short label of the current work, when any. */
|
|
17
|
+
readonly label?: string;
|
|
18
|
+
/** Detail fragment (path / command / pattern), when any. */
|
|
19
|
+
readonly detail?: string;
|
|
20
|
+
/** The playful phrase currently shown, when the copy pool is on. */
|
|
21
|
+
readonly phrase?: string;
|
|
22
|
+
/** Tools completed in the current turn. */
|
|
23
|
+
readonly toolCount: number;
|
|
24
|
+
/** Milliseconds since the current turn started (0 when idle). */
|
|
25
|
+
readonly turnElapsedMs: number;
|
|
26
|
+
/** Wall-clock time (epoch ms) the current phase started, for animations. */
|
|
27
|
+
readonly phaseStartedAt: number;
|
|
28
|
+
}
|
|
29
|
+
/** The `activity/status` phase vocabulary, exported for wire consumers. */
|
|
30
|
+
export type { ActivityPhase };
|
|
31
|
+
declare module '@deepseek-ai/dsh-session/types' {
|
|
32
|
+
interface SessionEventMap {
|
|
33
|
+
/**
|
|
34
|
+
* Log-only UI snapshot of the model's working activity (thinking copy,
|
|
35
|
+
* running tool, turn elapsed). Never a surface event: UIs render it, the
|
|
36
|
+
* model never sees it.
|
|
37
|
+
* @param data - The rendered status snapshot.
|
|
38
|
+
*/
|
|
39
|
+
'activity/status': ActivityStatusEvent;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD,yDAAyD;AACzD,MAAM,WAAW,mBAAmB;IAClC,4CAA4C;IAC5C,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,4EAA4E;IAC5E,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAChC;AAED,2EAA2E;AAC3E,YAAY,EAAE,aAAa,EAAE,CAAA;AAE7B,OAAO,QAAQ,gCAAgC,CAAC;IAC9C,UAAU,eAAe;QACvB;;;;;WAKG;QACH,iBAAiB,EAAE,mBAAmB,CAAA;KACvC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `activity/status` session event — a log-only, non-surface snapshot of the
|
|
3
|
+
* model's current working activity, published by this plugin for any UI
|
|
4
|
+
* consumer (Web client, telemetry, …). It never enters derived model history
|
|
5
|
+
* (no `surfaceOp`), so it cannot leak into prompts; UIs render it like
|
|
6
|
+
* `todo/write` or `plan/mode`.
|
|
7
|
+
* @module @deepseek-ai/dsh-working-activity/events
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* working-activity — a live "working line" for DeepSeek Harness agents.
|
|
3
|
+
*
|
|
4
|
+
* Folds the durable session stream (turn/step/tool/stream events) plus
|
|
5
|
+
* `agent/status` into a playful real-time status line, then publishes it two
|
|
6
|
+
* ways, both optional:
|
|
7
|
+
*
|
|
8
|
+
* - TUI: registers the `${activity}` prompt slot on `ctx.tuiPrompt` when the
|
|
9
|
+
* TUI is composed; add `${activity}` to `theme.leftPrompt` to see it.
|
|
10
|
+
* - Session log: appends log-only `activity/status` events (never surface
|
|
11
|
+
* events) for Web and other UI consumers; replay ignores them.
|
|
12
|
+
*
|
|
13
|
+
* The state machine itself lives in `./status.ts` (pure, clock-injected); this
|
|
14
|
+
* module only wires events, the render tick, and the two sinks.
|
|
15
|
+
* @module @deepseek-ai/dsh-working-activity
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import z from '@deepseek-ai/schemastery';
|
|
19
|
+
export type * from './events.js';
|
|
20
|
+
export declare const name = "working-activity";
|
|
21
|
+
/** Configurable knobs; every key has a sane default. */
|
|
22
|
+
export type Config = {
|
|
23
|
+
/** Playful copy pool; false renders plain functional labels. */
|
|
24
|
+
phrases?: boolean;
|
|
25
|
+
/** Append `activity/status` session events for UI consumers. */
|
|
26
|
+
publish?: boolean;
|
|
27
|
+
/** Status render tick interval in ms. */
|
|
28
|
+
tickMs?: number;
|
|
29
|
+
/** Minimum interval in ms between published events while the line is stable. */
|
|
30
|
+
publishIntervalMs?: number;
|
|
31
|
+
/** Maximum displayed detail length (paths/commands/patterns). */
|
|
32
|
+
detailLimit?: number;
|
|
33
|
+
/** Exact tool-name → action-copy pools (case-insensitive match). */
|
|
34
|
+
customActions?: Record<string, string[]>;
|
|
35
|
+
/** Inject the `⏵` self-narration contract into the system prompt and surface it. */
|
|
36
|
+
narrate?: boolean;
|
|
37
|
+
};
|
|
38
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
39
|
+
phrases: z<boolean, boolean>;
|
|
40
|
+
publish: z<boolean, boolean>;
|
|
41
|
+
tickMs: z<number, number>;
|
|
42
|
+
publishIntervalMs: z<number, number>;
|
|
43
|
+
detailLimit: z<number, number>;
|
|
44
|
+
customActions: z<import("@deepseek-ai/cosmokit").Dict<string[], string>, import("@deepseek-ai/cosmokit").Dict<string[], string>>;
|
|
45
|
+
narrate: z<boolean, boolean>;
|
|
46
|
+
}>, Schemastery.ObjectT<{
|
|
47
|
+
phrases: z<boolean, boolean>;
|
|
48
|
+
publish: z<boolean, boolean>;
|
|
49
|
+
tickMs: z<number, number>;
|
|
50
|
+
publishIntervalMs: z<number, number>;
|
|
51
|
+
detailLimit: z<number, number>;
|
|
52
|
+
customActions: z<import("@deepseek-ai/cosmokit").Dict<string[], string>, import("@deepseek-ai/cosmokit").Dict<string[], string>>;
|
|
53
|
+
narrate: z<boolean, boolean>;
|
|
54
|
+
}>>;
|
|
55
|
+
/**
|
|
56
|
+
* Wire the working-activity plugin.
|
|
57
|
+
* @param ctx - Cordis context (agent loop + session services composed).
|
|
58
|
+
* @param config - Validated plugin config (schema defaults applied).
|
|
59
|
+
*/
|
|
60
|
+
export declare function apply(ctx: Context, config?: Config): void;
|
|
61
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAWxC,mBAAmB,aAAa,CAAA;AAEhC,eAAO,MAAM,IAAI,qBAAqB,CAAA;AAEtC,wDAAwD;AACxD,MAAM,MAAM,MAAM,GAAG;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxC,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;GAQjB,CAAA;AAyBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAyH7D"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* working-activity — a live "working line" for DeepSeek Harness agents.
|
|
3
|
+
*
|
|
4
|
+
* Folds the durable session stream (turn/step/tool/stream events) plus
|
|
5
|
+
* `agent/status` into a playful real-time status line, then publishes it two
|
|
6
|
+
* ways, both optional:
|
|
7
|
+
*
|
|
8
|
+
* - TUI: registers the `${activity}` prompt slot on `ctx.tuiPrompt` when the
|
|
9
|
+
* TUI is composed; add `${activity}` to `theme.leftPrompt` to see it.
|
|
10
|
+
* - Session log: appends log-only `activity/status` events (never surface
|
|
11
|
+
* events) for Web and other UI consumers; replay ignores them.
|
|
12
|
+
*
|
|
13
|
+
* The state machine itself lives in `./status.ts` (pure, clock-injected); this
|
|
14
|
+
* module only wires events, the render tick, and the two sinks.
|
|
15
|
+
* @module @deepseek-ai/dsh-working-activity
|
|
16
|
+
*/
|
|
17
|
+
import z from '@deepseek-ai/schemastery';
|
|
18
|
+
import { ActivityTracker } from './status.js';
|
|
19
|
+
export const name = 'working-activity';
|
|
20
|
+
export const Config = z.object({
|
|
21
|
+
phrases: z.boolean().default(true),
|
|
22
|
+
publish: z.boolean().default(true),
|
|
23
|
+
tickMs: z.number().step(50).min(100).max(5000).default(500),
|
|
24
|
+
publishIntervalMs: z.number().step(500).min(500).max(30_000).default(2000),
|
|
25
|
+
detailLimit: z.number().step(1).min(8).max(120).default(40),
|
|
26
|
+
customActions: z.dict(z.array(z.string())).default({}),
|
|
27
|
+
narrate: z.boolean().default(true),
|
|
28
|
+
});
|
|
29
|
+
/** The self-narration contract injected into the system prompt (narrate on). */
|
|
30
|
+
const NARRATE_INSTRUCTION = '[状态栏] 你有一个状态栏展示给用户。【必须】在每个步骤/子任务开始时(不只是调用工具前),在回复正文的最前面单独写一行:⏵ 你在做的具体事情(不超过20字),然后换行继续正常回复。整轮回复只写一行 ⏵,不要重复。信息为主——让人一眼知道你在干什么,风格自然、可以带点俏皮。例:⏵ 修复登录页样式、⏵ 查一下报错原因、⏵ 给补丁跑个验证。切换任务时必须更新。';
|
|
31
|
+
/**
|
|
32
|
+
* Wire the working-activity plugin.
|
|
33
|
+
* @param ctx - Cordis context (agent loop + session services composed).
|
|
34
|
+
* @param config - Validated plugin config (schema defaults applied).
|
|
35
|
+
*/
|
|
36
|
+
export function apply(ctx, config = {}) {
|
|
37
|
+
const resolved = {
|
|
38
|
+
phrases: config.phrases ?? true,
|
|
39
|
+
publish: config.publish ?? true,
|
|
40
|
+
tickMs: config.tickMs ?? 500,
|
|
41
|
+
publishIntervalMs: config.publishIntervalMs ?? 2000,
|
|
42
|
+
detailLimit: config.detailLimit ?? 40,
|
|
43
|
+
narrate: config.narrate ?? true,
|
|
44
|
+
customActions: config.customActions ?? {},
|
|
45
|
+
};
|
|
46
|
+
const trackers = new Map();
|
|
47
|
+
let activeSession;
|
|
48
|
+
let lastPublishedLine;
|
|
49
|
+
let lastPublishedPhase;
|
|
50
|
+
let lastPublishAt = 0;
|
|
51
|
+
// Optional TUI seam: no TUI composed -> no slot, no error. The register()
|
|
52
|
+
// call is itself effect-owned, so fiber disposal unregisters the slot.
|
|
53
|
+
const prompt = ctx.get('tuiPrompt', false);
|
|
54
|
+
const promptHandle = prompt?.register('activity', undefined);
|
|
55
|
+
// The `⏵` self-narration contract rides the stable system-prompt sections:
|
|
56
|
+
// injected when the systemPrompt service is composed (agent assemblies
|
|
57
|
+
// always mount it), removed with this fiber.
|
|
58
|
+
if (resolved.narrate) {
|
|
59
|
+
ctx.inject(['systemPrompt'], (promptCtx) => {
|
|
60
|
+
promptCtx.systemPrompt.section({
|
|
61
|
+
name: 'working-activity:narrate',
|
|
62
|
+
order: 60,
|
|
63
|
+
text: NARRATE_INSTRUCTION,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const trackerFor = (session) => {
|
|
68
|
+
let tracker = trackers.get(session);
|
|
69
|
+
if (tracker === undefined) {
|
|
70
|
+
tracker = new ActivityTracker({ phrases: resolved.phrases, detailLimit: resolved.detailLimit, showIdle: false }, Date.now, resolved.customActions);
|
|
71
|
+
trackers.set(session, tracker);
|
|
72
|
+
}
|
|
73
|
+
return tracker;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Publish one rendered snapshot: TUI slot update + throttled session event.
|
|
77
|
+
* Callers snapshot the tracker state at event time and hand it here, so a
|
|
78
|
+
* burst of fast events (e.g. a synchronous tool call+result) cannot lose an
|
|
79
|
+
* intermediate phase; the append itself runs inside a microtask because the
|
|
80
|
+
* session's appending guard is still set while session/event callbacks run.
|
|
81
|
+
*/
|
|
82
|
+
const publish = (session, state) => {
|
|
83
|
+
queueMicrotask(() => {
|
|
84
|
+
const line = state.phase === 'idle' ? undefined : state.line;
|
|
85
|
+
promptHandle?.set(line);
|
|
86
|
+
if (!resolved.publish)
|
|
87
|
+
return;
|
|
88
|
+
const nowMs = Date.now();
|
|
89
|
+
const lineChanged = state.line !== lastPublishedLine;
|
|
90
|
+
const phaseChanged = state.phase !== lastPublishedPhase;
|
|
91
|
+
// Live phases republish on a throttle so elapsed times stay current;
|
|
92
|
+
// settled phases (idle/done) publish only when the line itself changes.
|
|
93
|
+
const liveThrottle = state.phase !== 'idle' && state.phase !== 'done'
|
|
94
|
+
&& nowMs - lastPublishAt >= resolved.publishIntervalMs;
|
|
95
|
+
if (!lineChanged && !phaseChanged && !liveThrottle)
|
|
96
|
+
return;
|
|
97
|
+
// Optional fields must be omitted (not undefined): session append rejects
|
|
98
|
+
// data JSON would discard, and `activity/status` is a lossless-JSON event.
|
|
99
|
+
const payload = {
|
|
100
|
+
phase: state.phase,
|
|
101
|
+
line: state.line,
|
|
102
|
+
toolCount: state.toolCount,
|
|
103
|
+
turnElapsedMs: state.turnElapsedMs,
|
|
104
|
+
phaseStartedAt: state.phaseStartedAt,
|
|
105
|
+
...(state.label === undefined ? {} : { label: state.label }),
|
|
106
|
+
...(state.detail === undefined ? {} : { detail: state.detail }),
|
|
107
|
+
...(state.phrase === undefined ? {} : { phrase: state.phrase }),
|
|
108
|
+
};
|
|
109
|
+
try {
|
|
110
|
+
session.append('activity/status', payload);
|
|
111
|
+
lastPublishedLine = state.line;
|
|
112
|
+
lastPublishedPhase = state.phase;
|
|
113
|
+
lastPublishAt = nowMs;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Session closed or the append guard still held: drop this snapshot;
|
|
117
|
+
// the next tick retries the same line.
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
ctx.on('session/event', (session, event) => {
|
|
122
|
+
const tracker = trackerFor(session);
|
|
123
|
+
tracker.onSessionEvent(event);
|
|
124
|
+
activeSession = session;
|
|
125
|
+
publish(session, tracker.render());
|
|
126
|
+
});
|
|
127
|
+
ctx.on('session/disposed', (session) => {
|
|
128
|
+
trackers.delete(session);
|
|
129
|
+
if (activeSession === session)
|
|
130
|
+
activeSession = undefined;
|
|
131
|
+
});
|
|
132
|
+
ctx.on('agent/status', ({ agent, status }) => {
|
|
133
|
+
const session = agent.session;
|
|
134
|
+
const tracker = trackerFor(session);
|
|
135
|
+
tracker.onAgentStatus(status);
|
|
136
|
+
activeSession = session;
|
|
137
|
+
publish(session, tracker.render());
|
|
138
|
+
});
|
|
139
|
+
// Continuous tick: elapsed times and the phrase rotation move on their own.
|
|
140
|
+
// A manual timer keeps this plugin free of the @cordisjs/plugin-timer mixin;
|
|
141
|
+
// the effect disposer clears it when this fiber unloads.
|
|
142
|
+
const tickTimer = setInterval(() => {
|
|
143
|
+
if (activeSession === undefined)
|
|
144
|
+
return;
|
|
145
|
+
const tracker = trackers.get(activeSession);
|
|
146
|
+
if (tracker === undefined)
|
|
147
|
+
return;
|
|
148
|
+
publish(activeSession, tracker.render());
|
|
149
|
+
}, resolved.tickMs);
|
|
150
|
+
ctx.effect(() => () => { clearInterval(tickTimer); }, 'working-activity tick timer');
|
|
151
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned `activity/status` snapshot invariants.
|
|
3
|
+
* @module dsh-working-activity/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "working-activity-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register the working-activity invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../../src/invariant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,oCAAoC;AACpC,eAAO,MAAM,IAAI,+BAA+B,CAAA;AAChD,2EAA2E;AAC3E,eAAO,MAAM,MAAM,UAAiB,CAAA;AAkDpC;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,IAAI,CACU,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned `activity/status` snapshot invariants.
|
|
3
|
+
* @module dsh-working-activity/invariant
|
|
4
|
+
*/
|
|
5
|
+
/** Cordis companion plugin name. */
|
|
6
|
+
export const name = 'working-activity-invariant';
|
|
7
|
+
/** Service required before the companion can reserve package ownership. */
|
|
8
|
+
export const inject = ['invariants'];
|
|
9
|
+
const PACKAGE_NAME = 'dsh-working-activity';
|
|
10
|
+
const PHASES = new Set(['idle', 'waiting', 'thinking', 'tool', 'done']);
|
|
11
|
+
/** Validate one published activity snapshot before it reaches the durable log. */
|
|
12
|
+
function validateStatus(data, fail) {
|
|
13
|
+
const record = data;
|
|
14
|
+
if (record === null || typeof record !== 'object' || Array.isArray(record)) {
|
|
15
|
+
fail('activity/status data must be an object');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (typeof record.phase !== 'string' || !PHASES.has(record.phase)) {
|
|
19
|
+
fail(`activity/status carries unknown phase ${JSON.stringify(record.phase)}`);
|
|
20
|
+
}
|
|
21
|
+
if (typeof record.line !== 'string' || record.line.length === 0) {
|
|
22
|
+
fail('activity/status line must be a non-empty string');
|
|
23
|
+
}
|
|
24
|
+
for (const key of ['toolCount', 'turnElapsedMs', 'phaseStartedAt']) {
|
|
25
|
+
const value = record[key];
|
|
26
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
27
|
+
fail(`activity/status ${key} must be a non-negative finite number`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
for (const key of ['label', 'detail', 'phrase']) {
|
|
31
|
+
if (record[key] !== undefined && typeof record[key] !== 'string') {
|
|
32
|
+
fail(`activity/status ${key} must be a string when present`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/* jscpd:ignore-start -- package companions share replay and dispatch plumbing */
|
|
37
|
+
/** Validate the package-owned event shape and ignore unrelated events. */
|
|
38
|
+
function validateEvent(event, fail) {
|
|
39
|
+
if (event.type === 'activity/status')
|
|
40
|
+
validateStatus(event.data, fail);
|
|
41
|
+
}
|
|
42
|
+
/** Install validation for loaded and newly appended activity snapshots. */
|
|
43
|
+
const install = Object.assign((ctx, fail) => {
|
|
44
|
+
for (const session of ctx.sessions.list()) {
|
|
45
|
+
for (const event of session.events)
|
|
46
|
+
validateEvent(event, fail);
|
|
47
|
+
}
|
|
48
|
+
ctx.on('internal/dispatch', (_mode, eventName, args) => {
|
|
49
|
+
if (eventName !== 'session/event')
|
|
50
|
+
return;
|
|
51
|
+
const event = args[1];
|
|
52
|
+
validateEvent(event, fail);
|
|
53
|
+
}, { global: true });
|
|
54
|
+
}, { inject: ['sessions'] });
|
|
55
|
+
/* jscpd:ignore-end */
|
|
56
|
+
/**
|
|
57
|
+
* Register the working-activity invariant companion.
|
|
58
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
59
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
60
|
+
*/
|
|
61
|
+
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy pools for the working-activity status line: short, colloquial, playful
|
|
3
|
+
* Chinese fragments with deadpan English one-liners mixed in, matching the
|
|
4
|
+
* pi-working-activity tone. Everything here is pure data + pure pickers.
|
|
5
|
+
* @module @deepseek-ai/dsh-working-activity/phrases
|
|
6
|
+
*/
|
|
7
|
+
/** A pool of copy fragments. */
|
|
8
|
+
export type PhrasePool = readonly string[];
|
|
9
|
+
/** Pick one random entry; repeated draws avoid the previous entry when possible. */
|
|
10
|
+
export declare function pickPhrase(entries: PhrasePool, previous?: string): string;
|
|
11
|
+
/** Thinking phrases while the model works without a tool. */
|
|
12
|
+
export declare const THINKING_PHRASES: readonly string[];
|
|
13
|
+
/** Tiered phrases when thinking runs long (elapsed >= threshold). */
|
|
14
|
+
export declare const THINKING_TIERS: readonly {
|
|
15
|
+
/** Minimum thinking ms for this tier. */
|
|
16
|
+
readonly atMs: number;
|
|
17
|
+
readonly pool: readonly string[];
|
|
18
|
+
}[];
|
|
19
|
+
/** Phrases shown while waiting for the first streamed token. */
|
|
20
|
+
export declare const WAITING_PHRASES: readonly string[];
|
|
21
|
+
/** Tool-name patterns mapped to playful action verbs. */
|
|
22
|
+
export declare const ACTION_MAP: readonly {
|
|
23
|
+
readonly test: RegExp;
|
|
24
|
+
readonly actions: readonly string[];
|
|
25
|
+
}[];
|
|
26
|
+
/** Fallback verbs for unknown tools. */
|
|
27
|
+
export declare const FALLBACK_ACTIONS: readonly string[];
|
|
28
|
+
/** Tool failure phrases, replacing a bare ✗. */
|
|
29
|
+
export declare const FAIL_PHRASES: readonly string[];
|
|
30
|
+
/** Turn-completion phrases. */
|
|
31
|
+
export declare const DONE_PHRASES: readonly string[];
|
|
32
|
+
/** Night-owl phrases mixed in between 00:00 and 06:00 local time. */
|
|
33
|
+
export declare const NIGHT_PHRASES: readonly string[];
|
|
34
|
+
/** Common git tool names / bash commands containing `git `. */
|
|
35
|
+
export declare const GIT_TOOL_RE: RegExp;
|
|
36
|
+
/** Detect the 00:00–06:00 night window (local time). */
|
|
37
|
+
export declare function isNight(hour: number): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Pick a thinking phrase appropriate for the elapsed thinking time.
|
|
40
|
+
* @param elapsedMs - Milliseconds spent thinking in the current phase.
|
|
41
|
+
* @param previous - Previously shown phrase, to avoid repeats.
|
|
42
|
+
* @param night - Mix night-owl copy into the pool.
|
|
43
|
+
*/
|
|
44
|
+
export declare function thinkingPhrase(elapsedMs: number, previous?: string, night?: boolean): string;
|
|
45
|
+
/**
|
|
46
|
+
* Map a tool name to a playful action verb.
|
|
47
|
+
* @param toolName - Registry tool name (unqualified).
|
|
48
|
+
* @param custom - Exact-name custom action pools, matched case-insensitively.
|
|
49
|
+
*/
|
|
50
|
+
export declare function actionFor(toolName: string, custom?: Readonly<Record<string, readonly string[]>>): string;
|
|
51
|
+
/** Whether a tool is a git operation (name match, or a shell command containing `git `). */
|
|
52
|
+
export declare function isGitTool(toolName: string, args?: Readonly<Record<string, unknown>>): boolean;
|
|
53
|
+
/** Format milliseconds as a compact human duration (`1m23s`). */
|
|
54
|
+
export declare function fmtDuration(ms: number): string;
|
|
55
|
+
//# sourceMappingURL=phrases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phrases.d.ts","sourceRoot":"","sources":["../../src/phrases.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,gCAAgC;AAChC,MAAM,MAAM,UAAU,GAAG,SAAS,MAAM,EAAE,CAAA;AAE1C,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CASzE;AAED,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAiB7C,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,cAAc,EAAE,SAAS;IACpC,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC,EAIA,CAAA;AAED,gEAAgE;AAChE,eAAO,MAAM,eAAe,EAAE,SAAS,MAAM,EAQ5C,CAAA;AAED,yDAAyD;AACzD,eAAO,MAAM,UAAU,EAAE,SAAS;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC,EAkBA,CAAA;AAED,wCAAwC;AACxC,eAAO,MAAM,gBAAgB,EAAE,SAAS,MAAM,EAAqD,CAAA;AAEnG,gDAAgD;AAChD,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAMzC,CAAA;AAED,+BAA+B;AAC/B,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAMzC,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAI1C,CAAA;AAED,+DAA+D;AAC/D,eAAO,MAAM,WAAW,QAA4G,CAAA;AAEpI,wDAAwD;AACxD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,UAAQ,GAAG,MAAM,CAY1F;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,GAAG,MAAM,CAQxG;AAED,4FAA4F;AAC5F,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAO7F;AAED,iEAAiE;AACjE,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAS9C"}
|