@dsh-cc/plugin-dsh-cc-shunt 0.8.0-rc.3 → 0.8.1

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.
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "dsh-cc-shunt",
3
3
  "description": "Official dsh-cc plugin: hard PreToolUse gates redirect bulk reads/boilerplate generation to cheap-lane shunt-worker subagents to save main-context tokens.",
4
- "version": "0.8.0-rc.3"
4
+ "version": "0.8.1"
5
5
  }
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm check:readme --write
5
- README.md: 937a8f850b1d498a61ab12b61ee36ed5503b1058
6
- README.zh.md: 2d68014bcbde12df912085cc4639b446a460a23b
5
+ README.md: af2dde8faba4b786c213fc515d15182f81c1809a
6
+ README.zh.md: dd7e01ae12856243617fdd46b1ad69231f2f52f3
package/README.md CHANGED
@@ -43,6 +43,24 @@ alias is unconfigured, the workers silently inherit the parent's model route
43
43
  — everything works, but you get **zero token savings**. Configure the haiku
44
44
  alias for actual savings.
45
45
 
46
+ ## Subagent exemption
47
+
48
+ Hook invocations that carry CC-parity caller identity (`agent_id` on the
49
+ PreToolUse payload) bypass both gates. The field is injected by the dsh-cc
50
+ bridge when the caller is a live subagent — it is not user-settable via
51
+ `tool_input` — so workers (critic/executor/marathon, shunt-reader,
52
+ shunt-writer, and any other subagent) paginate and inspect files freely;
53
+ the gate's value lives on the main thread, where the harness read caps
54
+ would make a block pure churn anyway.
55
+
56
+ ## Images
57
+
58
+ The Read gate sniffs magic bytes (PNG, JPEG, GIF, WEBP) before the
59
+ thresholds: `read_image` has no offset/limit, so large images are allowed
60
+ regardless of size, extensioned or extensionless. There is no extension
61
+ shortcut — a large **text** file named `*.png` is still gated. Any read
62
+ error falls through to the normal thresholds.
63
+
46
64
  ## Known limits
47
65
 
48
66
  - Digest line references can go stale after edits — verify with a targeted
package/README.zh.md CHANGED
@@ -33,6 +33,14 @@
33
33
 
34
34
  **shunt-worker 子代理固定 `model: haiku`。** 如果你的部署未配置 haiku 别名,worker 会静默继承父级的模型路由——一切照常工作,但**节省的 token 为零**。要真正省钱,请配置 haiku 别名。
35
35
 
36
+ ## 子代理豁免
37
+
38
+ 携带 CC 对齐调用者身份(PreToolUse 载荷上的 `agent_id`)的钩子调用会绕过两个门。该字段由 dsh-cc 桥接层在调用者是存活子代理时注入——不是用户可通过 `tool_input` 设置的——因此 worker(critic/executor/marathon、shunt-reader、shunt-writer 及其他子代理)可以自由分页和查看文件;门的价值在主线程,在那里 harness 读取上限本会让拦截变成纯粹的往返浪费。
39
+
40
+ ## 图片
41
+
42
+ Read 门在阈值之前按魔数(PNG、JPEG、GIF、WEBP)嗅探:`read_image` 没有 offset/limit,因此大图片无论多大都放行,带扩展名或不带扩展名均可。没有基于扩展名的捷径——名为 `*.png` 的大**文本**文件仍然会被拦截。任何读取错误都会回退到正常阈值。
43
+
36
44
  ## 已知限制
37
45
 
38
46
  - 摘要中的行号引用在编辑后可能失效——在引用位置编辑前,请先用带 offset/limit 的定向读取确认。
@@ -60,6 +60,10 @@ const command = typeof payload.tool_input?.command === 'string' ? payload.tool_i
60
60
  // Kill switch.
61
61
  if (truthy(process.env.SHUNT_DISABLED)) allow()
62
62
 
63
+ // Subagent exemption: invocations carrying CC-parity caller identity
64
+ // (agent_id, bridge-injected for live subagents) bypass the gate.
65
+ if (typeof payload.agent_id === 'string' && payload.agent_id !== '') allow()
66
+
63
67
  // No command, or a targeted (piped / redirected) command.
64
68
  if (!command || command.includes('|') || command.includes('>') || !READ_COMMAND_RE.test(command)) allow()
65
69
 
@@ -10,7 +10,7 @@
10
10
  * Allow contract: {"decision": "allow"} on stdout, exit 0.
11
11
  * Block contract: one-line {"decision": "block", "reason": "..."} on stdout, exit 0.
12
12
  */
13
- import { existsSync, readFileSync, statSync } from 'node:fs'
13
+ import { closeSync, existsSync, openSync, readFileSync, readSync, statSync } from 'node:fs'
14
14
 
15
15
  const DEFAULT_MIN_LINES = 350
16
16
  const DEFAULT_MAX_BYTES = 100000
@@ -61,6 +61,10 @@ const limit = toolInput.limit
61
61
  // Kill switch.
62
62
  if (truthy(process.env.SHUNT_DISABLED)) allow()
63
63
 
64
+ // Subagent exemption: invocations carrying CC-parity caller identity
65
+ // (agent_id, bridge-injected for live subagents) bypass the gate.
66
+ if (typeof payload.agent_id === 'string' && payload.agent_id !== '') allow()
67
+
64
68
  // Targeted reads always pass — the caller already knows what it needs.
65
69
  if (offset !== undefined || limit !== undefined) allow()
66
70
 
@@ -75,6 +79,31 @@ try {
75
79
  }
76
80
  if (!stat.isFile()) allow()
77
81
 
82
+ // Magic-byte image sniff: read_image has no offset/limit and accepts
83
+ // extensionless paths by content sniffing, so large images can never be
84
+ // paginated — allow real image content. No extension shortcut: a text file
85
+ // named *.png stays gated. Any read error falls through to the thresholds.
86
+ try {
87
+ const fd = openSync(filePath, 'r')
88
+ try {
89
+ const head = Buffer.alloc(12)
90
+ const n = readSync(fd, head, 0, 12, 0)
91
+ const b = head.subarray(0, n)
92
+ const isPng = n >= 8 && b.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
93
+ const isJpeg = n >= 3 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff
94
+ const isGif = n >= 4 && b.subarray(0, 4).equals(Buffer.from('GIF8', 'ascii'))
95
+ const isWebp =
96
+ n >= 12 &&
97
+ b.subarray(0, 4).equals(Buffer.from('RIFF', 'ascii')) &&
98
+ b.subarray(8, 12).equals(Buffer.from('WEBP', 'ascii'))
99
+ if (isPng || isJpeg || isGif || isWebp) allow()
100
+ } finally {
101
+ closeSync(fd)
102
+ }
103
+ } catch {
104
+ // fall through
105
+ }
106
+
78
107
  const minLines = intEnv('SHUNT_MIN_LINES', DEFAULT_MIN_LINES)
79
108
  const maxBytes = intEnv('SHUNT_MAX_BYTES', DEFAULT_MAX_BYTES)
80
109
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-cc/plugin-dsh-cc-shunt",
3
- "version": "0.8.0-rc.3",
3
+ "version": "0.8.1",
4
4
  "description": "Official dsh-cc plugin: hard PreToolUse gates redirect bulk reads and boilerplate generation to cheap-lane shunt-worker subagents to save main-context tokens.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",