Skip to content

Commit 38519c8

Browse files
committed
Add delegation timestamp and refresh worktree hints
1 parent 9c1436c commit 38519c8

File tree

6 files changed

+54
-0
lines changed

6 files changed

+54
-0
lines changed

plugin/gateway-core/dist/hooks/agent-context-shaper/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ function buildHint(context) {
3535
].join("\n");
3636
}
3737
function buildTaskFocusReminder(context) {
38+
const delegatedAt = new Date().toISOString();
3839
const trigger = Array.isArray(context.metadata.triggers) &&
3940
context.metadata.triggers.length > 0
4041
? context.metadata.triggers[0]
@@ -47,6 +48,7 @@ function buildTaskFocusReminder(context) {
4748
"[agent-context-shaper] delegated task focus",
4849
`- subagent: ${context.subagentType}`,
4950
`- category: ${context.category}`,
51+
`- delegated_at: ${delegatedAt}`,
5052
"- execute one delegated objective for this task call before returning control",
5153
`- prioritize: ${trigger}`,
5254
`- avoid: ${avoid}`,

plugin/gateway-core/dist/hooks/agent-model-resolver/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ function stripInjectedHeaders(original) {
131131
"MODEL ROUTING",
132132
"TOOL SURFACE",
133133
"SESSION FLOW",
134+
"WORKTREE CONTEXT",
134135
"THINKING EFFORT",
135136
]
136137
.reduce((text, header) => stripHeaderLine(text, header), original)

plugin/gateway-core/src/hooks/agent-context-shaper/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ function buildHint(context: StoredContext): string {
111111
}
112112

113113
function buildTaskFocusReminder(context: StoredContext): string {
114+
const delegatedAt = new Date().toISOString();
114115
const trigger =
115116
Array.isArray(context.metadata.triggers) &&
116117
context.metadata.triggers.length > 0
@@ -125,6 +126,7 @@ function buildTaskFocusReminder(context: StoredContext): string {
125126
"[agent-context-shaper] delegated task focus",
126127
`- subagent: ${context.subagentType}`,
127128
`- category: ${context.category}`,
129+
`- delegated_at: ${delegatedAt}`,
128130
"- execute one delegated objective for this task call before returning control",
129131
`- prioritize: ${trigger}`,
130132
`- avoid: ${avoid}`,

plugin/gateway-core/src/hooks/agent-model-resolver/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ function stripInjectedHeaders(original: string): string {
166166
"MODEL ROUTING",
167167
"TOOL SURFACE",
168168
"SESSION FLOW",
169+
"WORKTREE CONTEXT",
169170
"THINKING EFFORT",
170171
]
171172
.reduce((text, header) => stripHeaderLine(text, header), original)

plugin/gateway-core/test/agent-context-shaper-hook.test.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ test("agent-context-shaper prepends delegated task focus reminder once", async (
5959

6060
const text = String(output.args.prompt);
6161
assert.match(text, /\[agent-context-shaper\] delegated task focus/);
62+
assert.match(text, /- delegated_at: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/);
6263
assert.match(text, /execute one delegated objective/i);
6364
assert.match(text, /prioritize: map implementation locations/);
6465

@@ -70,6 +71,10 @@ test("agent-context-shaper prepends delegated task focus reminder once", async (
7071
(String(output.args.prompt).match(/delegated task focus/g) ?? []).length,
7172
1,
7273
);
74+
assert.equal(
75+
(String(output.args.prompt).match(/- delegated_at:/g) ?? []).length,
76+
1,
77+
);
7378
} finally {
7479
rmSync(directory, { recursive: true, force: true });
7580
}
@@ -100,6 +105,10 @@ test("agent-context-shaper still shapes trace-only delegations", async () => {
100105
String(output.args.prompt ?? ""),
101106
/\[agent-context-shaper\] delegated task focus/,
102107
);
108+
assert.match(
109+
String(output.args.prompt ?? ""),
110+
/- delegated_at: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/,
111+
);
103112
assert.match(String(output.args.prompt ?? ""), /\[DELEGATION TRACE/);
104113
} finally {
105114
rmSync(directory, { recursive: true, force: true });

plugin/gateway-core/test/agent-model-resolver-hook.test.mjs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,45 @@ test("agent-model-resolver prepends timestamped headers for delegated task descr
6565
}
6666
})
6767

68+
test("agent-model-resolver replaces WORKTREE CONTEXT when a delegation is reshaped", async () => {
69+
const firstDirectory = mkdtempSync(join(tmpdir(), "gateway-agent-model-resolver-first-"))
70+
const secondDirectory = mkdtempSync(join(tmpdir(), "gateway-agent-model-resolver-second-"))
71+
try {
72+
for (const directory of [firstDirectory, secondDirectory]) {
73+
const specsDir = join(directory, "agent", "specs")
74+
mkdirSync(specsDir, { recursive: true })
75+
writeFileSync(
76+
join(specsDir, "explore.json"),
77+
JSON.stringify({ name: "explore", metadata: { default_category: "quick" } }),
78+
"utf-8",
79+
)
80+
}
81+
82+
const initialPlugin = createPlugin(firstDirectory)
83+
const reroutedPlugin = createPlugin(secondDirectory)
84+
const output = {
85+
args: {
86+
subagent_type: "explore",
87+
description: "Scout repository patterns",
88+
prompt: "Inspect code paths",
89+
},
90+
}
91+
92+
await initialPlugin["tool.execute.before"]({ tool: "task", sessionID: "session-worktree-reroute" }, output)
93+
await reroutedPlugin["tool.execute.before"]({ tool: "task", sessionID: "session-worktree-reroute" }, output)
94+
95+
const prompt = String(output.args.prompt ?? "")
96+
const description = String(output.args.description ?? "")
97+
assert.equal((prompt.match(/^\[WORKTREE CONTEXT(?:\s+|\])/gm) ?? []).length, 1)
98+
assert.equal((description.match(/^\[WORKTREE CONTEXT(?:\s+|\])/gm) ?? []).length, 1)
99+
assert.match(prompt, new RegExp(`cwd=${secondDirectory.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`))
100+
assert.doesNotMatch(prompt, new RegExp(`cwd=${firstDirectory.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`))
101+
} finally {
102+
rmSync(firstDirectory, { recursive: true, force: true })
103+
rmSync(secondDirectory, { recursive: true, force: true })
104+
}
105+
})
106+
68107
test("agent-model-resolver infers explore delegation and category", async () => {
69108
const plugin = createPlugin(REPO_DIRECTORY)
70109
const output = {

0 commit comments

Comments
 (0)