typeclaw 0.15.1 → 0.15.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/package.json
CHANGED
|
@@ -71,11 +71,20 @@ export function createChannelReplyTool({
|
|
|
71
71
|
},
|
|
72
72
|
),
|
|
73
73
|
),
|
|
74
|
+
continue: Type.Optional(
|
|
75
|
+
Type.Boolean({
|
|
76
|
+
description:
|
|
77
|
+
'Set `true` ONLY when this reply is a mid-turn status update (e.g. "working on it…") and you still have work to do THIS turn — fetching data, running a tool, spawning a subagent, then replying again. ' +
|
|
78
|
+
'A normal reply omits this: by default a successful reply ends the turn (no wasted follow-up LLM call). ' +
|
|
79
|
+
'Do not set it just to seem responsive; only when genuine multi-step work follows in the same turn.',
|
|
80
|
+
}),
|
|
81
|
+
),
|
|
74
82
|
}),
|
|
75
83
|
|
|
76
84
|
async execute(_toolCallId, params) {
|
|
77
85
|
const text = params.text
|
|
78
86
|
const attachments = params.attachments
|
|
87
|
+
const keepTurnAlive = params.continue === true
|
|
79
88
|
if ((text === undefined || text === '') && (attachments === undefined || attachments.length === 0)) {
|
|
80
89
|
logger.warn(formatChannelToolFailure('channel_reply', 'missing text and attachments'))
|
|
81
90
|
return {
|
|
@@ -130,7 +139,14 @@ export function createChannelReplyTool({
|
|
|
130
139
|
),
|
|
131
140
|
)
|
|
132
141
|
}
|
|
133
|
-
|
|
142
|
+
// `continue` is read by the router's terminal hook (installChannelReplyTerminalHook),
|
|
143
|
+
// not by this tool — it suppresses the post-reply abort so a multi-step turn
|
|
144
|
+
// keeps going. Success-only: a denied reply never ran, so there is no turn to keep.
|
|
145
|
+
const details: { ok: boolean; error?: string; continue?: boolean } = result.ok
|
|
146
|
+
? keepTurnAlive
|
|
147
|
+
? { ok: true, continue: true }
|
|
148
|
+
: { ok: true }
|
|
149
|
+
: { ok: false, error: result.error }
|
|
134
150
|
// Echo the delivered text back to the model. The adapter classifier
|
|
135
151
|
// drops self-authored messages on the inbound path (`self_author`),
|
|
136
152
|
// so the bot otherwise has ZERO visibility into what it just said —
|
package/src/channels/router.ts
CHANGED
|
@@ -1248,16 +1248,22 @@ export function createChannelRouter(options: CreateChannelRouterOptions): Channe
|
|
|
1248
1248
|
// tools and `channel_send` must keep the follow-up so genuine multi-step turns
|
|
1249
1249
|
// continue. A prior non-typeclaw `afterToolCall` (none today) would be
|
|
1250
1250
|
// composed, not clobbered.
|
|
1251
|
+
//
|
|
1252
|
+
// `channel_reply({ continue: true })` is the explicit opt-out: a mid-turn
|
|
1253
|
+
// status reply ("working on it…") that the model follows with more work this
|
|
1254
|
+
// turn. The tool surfaces that intent as `details.continue === true`, and we
|
|
1255
|
+
// keep the follow-up so the turn proceeds. The kimi 32k loop only recurs when
|
|
1256
|
+
// the model genuinely has nothing left to say after a reply, which `continue`
|
|
1257
|
+
// asserts is not the case; Layer 2's maxTokens cap still bounds any misuse.
|
|
1251
1258
|
const installChannelReplyTerminalHook = (live: LiveSession): void => {
|
|
1252
1259
|
const { agent } = live.session
|
|
1253
1260
|
const prior = agent.afterToolCall
|
|
1254
1261
|
agent.afterToolCall = async (context, signal) => {
|
|
1255
1262
|
const result = prior ? await prior(context, signal) : undefined
|
|
1256
|
-
const
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
if (succeeded && agent.signal?.aborted !== true) {
|
|
1263
|
+
const details = context.result.details as { ok?: unknown; continue?: unknown } | undefined
|
|
1264
|
+
const succeeded = context.toolCall.name === 'channel_reply' && !context.isError && details?.ok === true
|
|
1265
|
+
const keepTurnAlive = details?.continue === true
|
|
1266
|
+
if (succeeded && !keepTurnAlive && agent.signal?.aborted !== true) {
|
|
1261
1267
|
logger.info(`[channels] ${live.keyId} terminal_after_channel_reply`)
|
|
1262
1268
|
agent.abort()
|
|
1263
1269
|
}
|
|
@@ -9,6 +9,10 @@ GitHub renders normal Markdown in issues, PRs, discussions, and review comments.
|
|
|
9
9
|
- There is no typing indicator.
|
|
10
10
|
- For PR review threads, keep `thread` set to reply in-place. Omit `thread` for a top-level PR/issue comment.
|
|
11
11
|
|
|
12
|
+
## Mid-turn status replies need `continue: true`
|
|
13
|
+
|
|
14
|
+
A successful `channel_reply` ends your turn by default — the runtime stops the model right after the reply lands. That is correct for a final answer, but it will **silently truncate** a turn that still has work to do. If you post a status line like "Reviewing now, I'll be back with findings" and then expect to keep working (fetch the diff, spawn the reviewer, post the review) in the **same** turn, you must call `channel_reply({ text: "…", continue: true })`. Without `continue: true`, the turn ends at that status reply and the review never runs. Reserve `continue: true` for genuine multi-step turns; the final reply that wraps up the turn omits it.
|
|
15
|
+
|
|
12
16
|
## Opening new issues and PRs
|
|
13
17
|
|
|
14
18
|
The `gh` CLI is pre-authenticated via `GH_TOKEN` (injected by the adapter at startup). Use it to open new issues or PRs:
|
|
@@ -39,6 +43,8 @@ Why delegate: the `reviewer` subagent runs on the `deep` model profile, loads a
|
|
|
39
43
|
|
|
40
44
|
2. **Spawn the `reviewer` subagent with the PR target.** Use `run_in_background: true` so you stay responsive while the deep model works. Pass the PR URL (or `owner/repo#N`) plus any context the requester gave you (focus areas, specific files, etc.) so the reviewer knows what the requester cares about.
|
|
41
45
|
|
|
46
|
+
If you post an "on it" acknowledgement before fetching the diff or spawning the reviewer, it **must** be `channel_reply({ text: "…", continue: true })` — a bare reply ends the turn and the review never starts (see "Mid-turn status replies need `continue: true`" above).
|
|
47
|
+
|
|
42
48
|
The reviewer will fetch the diff itself (`gh pr diff`, `gh api /repos/.../pulls/<n>`), load the matching skill (`code-review` for a code PR; `general` for a mixed-format change), and return a `<review>` block.
|
|
43
49
|
|
|
44
50
|
3. **Wait for the completion `<system-reminder>`,** then call `subagent_output({ task_id })` to read the reviewer's final assistant message. The structured payload looks like:
|