@dungle-scrubs/tallow 0.8.25 → 0.8.26
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/dist/auth-hardening.d.ts +12 -0
- package/dist/auth-hardening.d.ts.map +1 -1
- package/dist/auth-hardening.js +30 -7
- package/dist/auth-hardening.js.map +1 -1
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/install.js +2 -2
- package/dist/install.js.map +1 -1
- package/dist/interactive-mode-patch.d.ts.map +1 -1
- package/dist/interactive-mode-patch.js +119 -7
- package/dist/interactive-mode-patch.js.map +1 -1
- package/dist/model-metadata-overrides.d.ts +19 -0
- package/dist/model-metadata-overrides.d.ts.map +1 -0
- package/dist/model-metadata-overrides.js +38 -0
- package/dist/model-metadata-overrides.js.map +1 -0
- package/dist/sdk.d.ts +2 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +28 -1
- package/dist/sdk.js.map +1 -1
- package/extensions/__integration__/teams-runtime.test.ts +22 -1
- package/extensions/_shared/__tests__/shell-policy.test.ts +197 -0
- package/extensions/_shared/shell-policy.ts +27 -0
- package/extensions/background-task-tool/index.ts +2 -1
- package/extensions/bash-tool-enhanced/index.ts +2 -1
- package/extensions/custom-footer/__tests__/index.test.ts +29 -0
- package/extensions/custom-footer/context-display.ts +49 -0
- package/extensions/custom-footer/index.ts +10 -23
- package/extensions/permissions/index.ts +31 -10
- package/extensions/plan-mode-tool/__tests__/index.test.ts +32 -2
- package/extensions/plan-mode-tool/index.ts +6 -1
- package/extensions/slash-command-bridge/index.ts +30 -1
- package/extensions/subagent-tool/__tests__/process-liveness.test.ts +42 -3
- package/extensions/subagent-tool/process.ts +132 -21
- package/extensions/tasks/__tests__/store.test.ts +26 -2
- package/extensions/tasks/commands/register-tasks-extension.ts +2 -2
- package/extensions/tasks/index.ts +5 -5
- package/extensions/tasks/state/index.ts +90 -36
- package/extensions/teams-tool/__tests__/archive-store.test.ts +98 -0
- package/extensions/teams-tool/__tests__/peer-messaging.test.ts +26 -0
- package/extensions/teams-tool/archive-store.ts +200 -0
- package/extensions/teams-tool/sessions/spawn.ts +244 -71
- package/extensions/teams-tool/tools/register-extension.ts +146 -105
- package/extensions/teams-tool/tools/teammate-tools.ts +43 -1
- package/package.json +4 -4
- package/skills/tallow-expert/SKILL.md +1 -1
- package/templates/agents/architect.md +13 -5
- package/templates/agents/debug.md +3 -3
- package/templates/agents/explore.md +9 -2
- package/templates/agents/refactor.md +2 -2
- package/templates/agents/scout.md +3 -2
|
@@ -10,70 +10,100 @@ import {
|
|
|
10
10
|
AuthStorage,
|
|
11
11
|
createAgentSession,
|
|
12
12
|
createExtensionRuntime,
|
|
13
|
+
DefaultResourceLoader,
|
|
13
14
|
ModelRegistry,
|
|
14
|
-
type ResourceLoader,
|
|
15
15
|
SessionManager,
|
|
16
16
|
SettingsManager,
|
|
17
17
|
} from "@mariozechner/pi-coding-agent";
|
|
18
|
-
import {
|
|
18
|
+
import { applyKnownModelMetadataOverrides } from "../../../src/model-metadata-overrides.js";
|
|
19
|
+
import { getTallowHomeDir, getTallowPath } from "../../_shared/tallow-paths.js";
|
|
20
|
+
import {
|
|
21
|
+
type AgentConfig,
|
|
22
|
+
computeEffectiveTools,
|
|
23
|
+
discoverAgents,
|
|
24
|
+
resolveAgentForExecution,
|
|
25
|
+
} from "../../subagent-tool/agents.js";
|
|
19
26
|
import { type RoutingHints, routeModel } from "../../subagent-tool/model-router.js";
|
|
20
27
|
import { resolveStandardTools } from "../state/team-view.js";
|
|
21
28
|
import type { Teammate } from "../state/types.js";
|
|
22
29
|
import type { Team } from "../store.js";
|
|
23
30
|
import { createTeammateTools } from "../tools/teammate-tools.js";
|
|
24
31
|
|
|
32
|
+
interface SpawnTeammateSessionOptions {
|
|
33
|
+
readonly agentName?: string;
|
|
34
|
+
readonly cwd: string;
|
|
35
|
+
readonly hints?: RoutingHints;
|
|
36
|
+
readonly modelOverride?: string;
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly parentModelId?: string;
|
|
39
|
+
readonly piEvents?: ExtensionAPI["events"];
|
|
40
|
+
readonly role?: string;
|
|
41
|
+
readonly team: Team<Teammate>;
|
|
42
|
+
readonly thinkingLevel?: string;
|
|
43
|
+
readonly toolNames?: string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type TeammateThinkingLevel = "high" | "low" | "medium" | "off";
|
|
47
|
+
|
|
25
48
|
/**
|
|
26
|
-
*
|
|
49
|
+
* Coerce an arbitrary string into a supported teammate thinking level.
|
|
27
50
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* - modelScope set → auto-route within that model family
|
|
31
|
-
* - neither → full auto-route based on role complexity and cost preference
|
|
32
|
-
*
|
|
33
|
-
* @param cwd - Working directory
|
|
34
|
-
* @param team - Team to add the teammate to
|
|
35
|
-
* @param name - Teammate name
|
|
36
|
-
* @param role - Role description (used for task classification + system prompt)
|
|
37
|
-
* @param modelOverride - Explicit model name (fuzzy matched). Skips auto-routing.
|
|
38
|
-
* @param toolNames - Standard tool names (defaults to all coding tools)
|
|
39
|
-
* @param piEvents - Event emitter for lifecycle events
|
|
40
|
-
* @param hints - Optional routing hints (modelScope, costPreference, etc.)
|
|
41
|
-
* @param parentModelId - Parent model ID for fallback inheritance
|
|
42
|
-
* @returns The created Teammate
|
|
43
|
-
* @throws If model not found or session creation fails
|
|
51
|
+
* @param value - Raw level string from caller context or tool params
|
|
52
|
+
* @returns Supported thinking level, or undefined when unsupported
|
|
44
53
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
): Promise<Teammate> {
|
|
56
|
-
const routing = await routeModel(role, modelOverride, undefined, parentModelId, role, hints, cwd);
|
|
57
|
-
if (!routing.ok) {
|
|
58
|
-
const available = listAvailableModels().slice(0, 20).join(", ");
|
|
59
|
-
throw new Error(`Model not found: "${routing.query}". Available: ${available}`);
|
|
54
|
+
function coerceThinkingLevel(value: string | undefined): TeammateThinkingLevel | undefined {
|
|
55
|
+
if (!value) return undefined;
|
|
56
|
+
const normalized = value.trim().toLowerCase();
|
|
57
|
+
if (
|
|
58
|
+
normalized === "off" ||
|
|
59
|
+
normalized === "low" ||
|
|
60
|
+
normalized === "medium" ||
|
|
61
|
+
normalized === "high"
|
|
62
|
+
) {
|
|
63
|
+
return normalized;
|
|
60
64
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// Use the user's tallow auth and model config so teammates inherit
|
|
64
|
-
// API keys and custom model definitions from the main session.
|
|
65
|
-
const authStorage = AuthStorage.create(getTallowPath("auth.json"));
|
|
66
|
-
const modelRegistry = new ModelRegistry(authStorage, getTallowPath("models.json"));
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Resolve an optional teammate agent template from the shared agent directories.
|
|
70
|
+
*
|
|
71
|
+
* Team teammates should not silently fall back to an ephemeral template when the
|
|
72
|
+
* caller explicitly requested a named agent. Typos must fail closed.
|
|
73
|
+
*
|
|
74
|
+
* @param cwd - Working directory used for project-agent discovery
|
|
75
|
+
* @param agentName - Optional template name requested by the caller
|
|
76
|
+
* @returns Resolved agent template, or undefined when no template was requested
|
|
77
|
+
* @throws {Error} When a named template cannot be found
|
|
78
|
+
*/
|
|
79
|
+
function resolveTeammateAgentTemplate(
|
|
80
|
+
cwd: string,
|
|
81
|
+
agentName: string | undefined
|
|
82
|
+
): AgentConfig | undefined {
|
|
83
|
+
if (!agentName) return undefined;
|
|
84
|
+
const discovery = discoverAgents(cwd, "both");
|
|
85
|
+
const resolved = resolveAgentForExecution(agentName, discovery.agents, discovery.defaults);
|
|
86
|
+
if (resolved.resolution === "ephemeral") {
|
|
70
87
|
throw new Error(
|
|
71
|
-
`
|
|
88
|
+
`Teammate agent template "${agentName}" was not found in user or project agent directories.`
|
|
72
89
|
);
|
|
73
90
|
}
|
|
91
|
+
return resolved.agent;
|
|
92
|
+
}
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Build the coordination prompt appended to every teammate.
|
|
96
|
+
*
|
|
97
|
+
* @param team - Runtime team container
|
|
98
|
+
* @param name - Teammate name
|
|
99
|
+
* @param role - Effective teammate role text
|
|
100
|
+
* @returns Coordination instructions shared by all teammates
|
|
101
|
+
*/
|
|
102
|
+
function buildCoordinationPrompt(team: Team<Teammate>, name: string, role: string): string {
|
|
103
|
+
const otherNames = Array.from(team.teammates.keys()).filter(
|
|
104
|
+
(teammateName) => teammateName !== name
|
|
105
|
+
);
|
|
106
|
+
return [
|
|
77
107
|
`You are "${name}", a teammate in team "${team.name}".`,
|
|
78
108
|
`Your role: ${role}`,
|
|
79
109
|
"",
|
|
@@ -95,40 +125,183 @@ export async function spawnTeammateSession(
|
|
|
95
125
|
"",
|
|
96
126
|
"Communicate with teammates via team_message when you need their input.",
|
|
97
127
|
].join("\n");
|
|
128
|
+
}
|
|
98
129
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Merge teammate coordination with an optional agent template system prompt.
|
|
132
|
+
*
|
|
133
|
+
* @param team - Runtime team container
|
|
134
|
+
* @param name - Teammate name
|
|
135
|
+
* @param role - Effective teammate role text
|
|
136
|
+
* @param templateAgent - Optional resolved agent template
|
|
137
|
+
* @returns Final system prompt text for the teammate session
|
|
138
|
+
*/
|
|
139
|
+
function buildTeammateSystemPrompt(
|
|
140
|
+
team: Team<Teammate>,
|
|
141
|
+
name: string,
|
|
142
|
+
role: string,
|
|
143
|
+
templateAgent: AgentConfig | undefined
|
|
144
|
+
): string {
|
|
145
|
+
const sections = [buildCoordinationPrompt(team, name, role)];
|
|
146
|
+
if (templateAgent?.systemPrompt.trim()) {
|
|
147
|
+
sections.push(
|
|
148
|
+
[`Base agent template: ${templateAgent.name}`, templateAgent.systemPrompt.trim()].join("\n\n")
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (templateAgent?.maxTurns) {
|
|
152
|
+
sections.unshift(
|
|
153
|
+
`You have a maximum of ${templateAgent.maxTurns} tool-use turns for this task. Plan accordingly and return your best partial result before hitting the limit.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return sections.join("\n\n");
|
|
157
|
+
}
|
|
111
158
|
|
|
112
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Resolve the standard tool allowlist for a teammate.
|
|
161
|
+
*
|
|
162
|
+
* Explicit tool names passed to `team_spawn` win. Otherwise agent-template
|
|
163
|
+
* allow/deny lists are applied when present.
|
|
164
|
+
*
|
|
165
|
+
* @param explicitToolNames - Tools passed directly to team_spawn
|
|
166
|
+
* @param templateAgent - Optional resolved agent template
|
|
167
|
+
* @returns Effective tool names, or undefined to allow the default coding set
|
|
168
|
+
*/
|
|
169
|
+
function resolveTeammateToolNames(
|
|
170
|
+
explicitToolNames: string[] | undefined,
|
|
171
|
+
templateAgent: AgentConfig | undefined
|
|
172
|
+
): string[] | undefined {
|
|
173
|
+
if (explicitToolNames) return explicitToolNames;
|
|
174
|
+
if (!templateAgent) return undefined;
|
|
175
|
+
return computeEffectiveTools(templateAgent.tools, templateAgent.disallowedTools);
|
|
176
|
+
}
|
|
113
177
|
|
|
114
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Create a loader that only exposes the requested teammate skills/system prompt.
|
|
180
|
+
*
|
|
181
|
+
* Extensions are disabled on purpose — teammates get only the explicitly passed
|
|
182
|
+
* standard tools plus the injected team coordination tools.
|
|
183
|
+
*
|
|
184
|
+
* @param cwd - Working directory
|
|
185
|
+
* @param settingsManager - Shared in-memory settings for the teammate session
|
|
186
|
+
* @param systemPrompt - Final system prompt text
|
|
187
|
+
* @param templateAgent - Optional resolved agent template
|
|
188
|
+
* @returns Reloaded resource loader ready for createAgentSession
|
|
189
|
+
*/
|
|
190
|
+
async function createTeammateResourceLoader(
|
|
191
|
+
cwd: string,
|
|
192
|
+
settingsManager: SettingsManager,
|
|
193
|
+
systemPrompt: string,
|
|
194
|
+
templateAgent: AgentConfig | undefined
|
|
195
|
+
): Promise<DefaultResourceLoader> {
|
|
196
|
+
const requestedSkills = new Set(templateAgent?.skills ?? []);
|
|
197
|
+
const loader = new DefaultResourceLoader({
|
|
198
|
+
agentDir: getTallowHomeDir(),
|
|
115
199
|
cwd,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
200
|
+
extensionsOverride: () => ({ extensions: [], errors: [], runtime: createExtensionRuntime() }),
|
|
201
|
+
promptsOverride: () => ({ diagnostics: [], prompts: [] }),
|
|
202
|
+
settingsManager,
|
|
203
|
+
skillsOverride: (base) => ({
|
|
204
|
+
diagnostics: base.diagnostics,
|
|
205
|
+
skills:
|
|
206
|
+
requestedSkills.size === 0
|
|
207
|
+
? []
|
|
208
|
+
: base.skills.filter((skill) => requestedSkills.has(skill.name)),
|
|
209
|
+
}),
|
|
210
|
+
systemPromptOverride: () => systemPrompt,
|
|
211
|
+
});
|
|
212
|
+
await loader.reload();
|
|
213
|
+
return loader;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Spawn a teammate as an in-process AgentSession with shared team tools.
|
|
218
|
+
*
|
|
219
|
+
* Model selection follows the same routing as subagents:
|
|
220
|
+
* - modelOverride set → explicit fuzzy resolution (best match)
|
|
221
|
+
* - template agent model set → explicit or auto-routing keyword from frontmatter
|
|
222
|
+
* - neither → full auto-route based on role complexity and cost preference
|
|
223
|
+
*
|
|
224
|
+
* @param options - Session spawn options
|
|
225
|
+
* @returns The created Teammate
|
|
226
|
+
* @throws {Error} If model or agent template resolution fails
|
|
227
|
+
*/
|
|
228
|
+
export async function spawnTeammateSession(
|
|
229
|
+
options: SpawnTeammateSessionOptions
|
|
230
|
+
): Promise<Teammate> {
|
|
231
|
+
const templateAgent = resolveTeammateAgentTemplate(options.cwd, options.agentName);
|
|
232
|
+
const role = options.role?.trim() || templateAgent?.description?.trim();
|
|
233
|
+
if (!role) {
|
|
234
|
+
throw new Error("team_spawn requires either a role or an agent template.");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const routingTask = templateAgent?.systemPrompt?.trim()
|
|
238
|
+
? `${role}\n\n${templateAgent.systemPrompt.trim()}`
|
|
239
|
+
: role;
|
|
240
|
+
const routing = await routeModel(
|
|
241
|
+
routingTask,
|
|
242
|
+
options.modelOverride,
|
|
243
|
+
templateAgent?.model,
|
|
244
|
+
options.parentModelId,
|
|
245
|
+
role,
|
|
246
|
+
options.hints,
|
|
247
|
+
options.cwd
|
|
248
|
+
);
|
|
249
|
+
if (!routing.ok) {
|
|
250
|
+
const available = listAvailableModels().slice(0, 20).join(", ");
|
|
251
|
+
throw new Error(`Model not found: "${routing.query}". Available: ${available}`);
|
|
252
|
+
}
|
|
253
|
+
const resolvedModel = routing.model;
|
|
254
|
+
|
|
255
|
+
// Use the user's tallow auth and model config so teammates inherit
|
|
256
|
+
// API keys and custom model definitions from the main session.
|
|
257
|
+
const authStorage = AuthStorage.create(getTallowPath("auth.json"));
|
|
258
|
+
const modelRegistry = new ModelRegistry(authStorage, getTallowPath("models.json"));
|
|
259
|
+
applyKnownModelMetadataOverrides(modelRegistry);
|
|
260
|
+
const model = modelRegistry.find(resolvedModel.provider, resolvedModel.id);
|
|
261
|
+
if (!model) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
`Model resolved to "${resolvedModel.id}" (provider: ${resolvedModel.provider}) but not found in registry`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const systemPrompt = buildTeammateSystemPrompt(options.team, options.name, role, templateAgent);
|
|
268
|
+
const settingsManager = SettingsManager.inMemory({
|
|
269
|
+
compaction: { enabled: true },
|
|
270
|
+
retry: { enabled: true, maxRetries: 2 },
|
|
271
|
+
});
|
|
272
|
+
const resourceLoader = await createTeammateResourceLoader(
|
|
273
|
+
options.cwd,
|
|
274
|
+
settingsManager,
|
|
275
|
+
systemPrompt,
|
|
276
|
+
templateAgent
|
|
277
|
+
);
|
|
278
|
+
const teammateCustomTools = createTeammateTools(options.team, options.name, options.piEvents);
|
|
279
|
+
const thinkingLevel = coerceThinkingLevel(options.thinkingLevel) ?? "off";
|
|
280
|
+
const toolNames = resolveTeammateToolNames(options.toolNames, templateAgent);
|
|
281
|
+
const standardTools =
|
|
282
|
+
toolNames && toolNames.length === 0 ? [] : resolveStandardTools(options.cwd, toolNames);
|
|
283
|
+
|
|
284
|
+
const { session } = await createAgentSession({
|
|
119
285
|
authStorage,
|
|
286
|
+
customTools: teammateCustomTools,
|
|
287
|
+
cwd: options.cwd,
|
|
288
|
+
agentDir: path.join(os.tmpdir(), `pi-team-${options.team.name}-${options.name}`),
|
|
289
|
+
model,
|
|
120
290
|
modelRegistry,
|
|
121
291
|
resourceLoader,
|
|
122
|
-
tools: resolveStandardTools(cwd, toolNames),
|
|
123
|
-
customTools: teammateCustomTools,
|
|
124
292
|
sessionManager: SessionManager.inMemory(),
|
|
125
|
-
settingsManager
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}),
|
|
293
|
+
settingsManager,
|
|
294
|
+
thinkingLevel,
|
|
295
|
+
tools: standardTools,
|
|
129
296
|
});
|
|
130
297
|
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
298
|
+
const teammate: Teammate = {
|
|
299
|
+
model: resolvedModel.id,
|
|
300
|
+
name: options.name,
|
|
301
|
+
role,
|
|
302
|
+
session,
|
|
303
|
+
status: "idle",
|
|
304
|
+
};
|
|
305
|
+
options.team.teammates.set(options.name, teammate);
|
|
306
|
+
return teammate;
|
|
134
307
|
}
|