@xingular/harness 2.0.0 → 2.1.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.
- package/CHANGELOG.md +10 -0
- package/README.md +7 -3
- package/dist/cli.js +8709 -8145
- package/dist/terminal-pi.js +79 -18
- package/docs/decisions/ADR-0003-shared-asynchronous-subagents.md +3 -1
- package/docs/terminal-tui.md +19 -13
- package/package.json +1 -1
- package/src/hub/hub.test.ts +49 -1
- package/src/hub/hub.ts +122 -6
- package/src/hub/schema.ts +13 -1
- package/src/server/methods.ts +2 -0
- package/src/sessions/planMode.test.ts +40 -0
- package/src/sessions/planMode.ts +57 -0
- package/src/sessions/sessions.test.ts +23 -0
- package/src/sessions/sessions.ts +17 -1
- package/src/terminal/piExtension.lifecycle.test.ts +28 -1
- package/src/terminal/piExtension.ts +15 -1
- package/src/terminal/tui/application.test.ts +31 -14
- package/src/terminal/tui/application.ts +75 -21
- package/src/terminal/tui/commands.test.ts +13 -0
- package/src/terminal/tui/commands.ts +7 -0
- package/src/terminal/tui/editor.ts +16 -1
- package/src/terminal/tui/keybindings.test.ts +1 -2
- package/src/terminal/tui/keybindings.ts +1 -2
- package/src/terminal/tui/planMode.ts +2 -12
- package/src/terminal/tui/runtime.ts +24 -2
- package/src/tools/types.ts +2 -0
|
@@ -1,12 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// Read-only inspection, questions and recall. Everything else is withdrawn in plan mode: file changes, commands,
|
|
4
|
-
// memory writes, delegation and connected apps, since none of them can be judged read-only from their names.
|
|
5
|
-
const READ_ONLY_TOOLS = new Set([
|
|
6
|
-
'read', 'grep', 'find', 'ls', ASK_USER, 'ask_user_question',
|
|
7
|
-
'history_search', 'history_read', 'wiki_entrypoint', 'wiki_search', 'wiki_read', 'wiki_list', 'memory_facts'
|
|
8
|
-
])
|
|
9
|
-
|
|
10
|
-
export function planModeTools(normal: readonly string[]): string[] {
|
|
11
|
-
return normal.filter(name => READ_ONLY_TOOLS.has(name))
|
|
12
|
-
}
|
|
1
|
+
// The terminal's plan mode uses the session's read-only tool set.
|
|
2
|
+
export { planModeTools } from '../../sessions/planMode'
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
import { closeSync, existsSync, openSync, readSync, realpathSync } from 'node:fs'
|
|
8
8
|
import { isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
9
9
|
|
|
10
|
-
import terminalExtension from '../piExtension'
|
|
10
|
+
import terminalExtension, { type TerminalPlanRunState } from '../piExtension'
|
|
11
|
+
import { planModeTools } from './planMode'
|
|
11
12
|
import { claimTerminalControl, controlCall } from '../control'
|
|
12
13
|
import { loadPiAdapter } from './piAdapter'
|
|
13
14
|
import type { SessionAccount } from '../accountsMenu'
|
|
@@ -101,6 +102,7 @@ export interface TerminalRuntime {
|
|
|
101
102
|
answerBatch(batchId: string, answers: Array<{ questionId: string; answer: QuestionAnswer }>): Promise<void>
|
|
102
103
|
snapshot(): TerminalSnapshot
|
|
103
104
|
submit(text: string, followUp?: boolean): Promise<void>
|
|
105
|
+
submitPlan(text: string): Promise<void>
|
|
104
106
|
readonly session: AgentSession
|
|
105
107
|
readonly services: AgentSessionServices
|
|
106
108
|
readonly cwd: string
|
|
@@ -200,6 +202,7 @@ export async function createTerminalRuntime(args: string[], host: TerminalRuntim
|
|
|
200
202
|
const generation = uiGeneration
|
|
201
203
|
return sessionUiContext(host.ui, () => !disposed && generation === uiGeneration)
|
|
202
204
|
}
|
|
205
|
+
const planRun: TerminalPlanRunState = { active: false, allowedTools: new Set() }
|
|
203
206
|
|
|
204
207
|
const factory: CreateAgentSessionRuntimeFactory = async ({ cwd: targetCwd, sessionManager, sessionStartEvent }) => {
|
|
205
208
|
if (realpathSync(targetCwd) !== cwd) throw new Error('A native session cannot change this terminal working folder.')
|
|
@@ -213,7 +216,7 @@ export async function createTerminalRuntime(args: string[], host: TerminalRuntim
|
|
|
213
216
|
additionalExtensionPaths: prepared.extensions.slice(0, -1),
|
|
214
217
|
additionalSkillPaths: prepared.skills, additionalPromptTemplatePaths: prepared.prompts,
|
|
215
218
|
additionalThemePaths: prepared.themes,
|
|
216
|
-
extensionFactories: [...adapter.builtins, { name: 'harness-terminal-bridge', factory: terminalExtension, hidden: true }],
|
|
219
|
+
extensionFactories: [...adapter.builtins, { name: 'harness-terminal-bridge', factory: pi => terminalExtension(pi, planRun), hidden: true }],
|
|
217
220
|
appendSystemPromptOverride: (base) => [...base, ...prepared.appendSystemPrompt]
|
|
218
221
|
})
|
|
219
222
|
const reloadResources = resourceLoader.reload.bind(resourceLoader)
|
|
@@ -309,6 +312,25 @@ export async function createTerminalRuntime(args: string[], host: TerminalRuntim
|
|
|
309
312
|
const input = text.trimStart().startsWith('/') ? text.trimStart() : text
|
|
310
313
|
await native.session.prompt(input, native.session.isStreaming ? { streamingBehavior: followUp ? 'followUp' : 'steer' } : {})
|
|
311
314
|
},
|
|
315
|
+
async submitPlan(text) {
|
|
316
|
+
if (disposed) throw new Error('The terminal runtime has closed.')
|
|
317
|
+
if (changing) throw new Error('Wait for the native session change to finish before submitting.')
|
|
318
|
+
if (!text.trim()) throw new Error('Use /plan <plan brief>.')
|
|
319
|
+
const session = native.session
|
|
320
|
+
if (!session.isIdle) throw new Error('A plan request must wait until the current response settles.')
|
|
321
|
+
const originalTools = session.getActiveToolNames()
|
|
322
|
+
const allowedTools = planModeTools(originalTools)
|
|
323
|
+
planRun.allowedTools = new Set(allowedTools)
|
|
324
|
+
planRun.active = true
|
|
325
|
+
try {
|
|
326
|
+
session.setActiveToolsByName(allowedTools)
|
|
327
|
+
await session.prompt(text, { expandPromptTemplates: false })
|
|
328
|
+
} finally {
|
|
329
|
+
planRun.active = false
|
|
330
|
+
planRun.allowedTools = new Set()
|
|
331
|
+
if (!disposed && native.session === session) session.setActiveToolsByName(originalTools)
|
|
332
|
+
}
|
|
333
|
+
},
|
|
312
334
|
get session() { return native.session },
|
|
313
335
|
get services() { return native.services },
|
|
314
336
|
cwd, sessionDirectory,
|
package/src/tools/types.ts
CHANGED
|
@@ -20,6 +20,8 @@ export interface ToolSpec {
|
|
|
20
20
|
description: string
|
|
21
21
|
/** Builds the TypeBox parameter schema. */
|
|
22
22
|
parameters: (type: typeof import('typebox').Type) => unknown
|
|
23
|
+
/** Only reads or asks; such a tool stays callable in plan mode. */
|
|
24
|
+
readOnly?: boolean
|
|
23
25
|
execute: (input: Record<string, unknown>) => Promise<string | ToolResult>
|
|
24
26
|
}
|
|
25
27
|
|