@agimon-ai/po-hook 0.0.0-alpha.0
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/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/apiContracts.cjs +17 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +13 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/authoring.cjs +10 -0
- package/dist/authoring.cjs.map +1 -0
- package/dist/authoring.d.mts +7 -0
- package/dist/authoring.d.mts.map +1 -0
- package/dist/authoring.mjs +9 -0
- package/dist/authoring.mjs.map +1 -0
- package/dist/extensions/mcp.cjs +26 -0
- package/dist/extensions/mcp.cjs.map +1 -0
- package/dist/extensions/mcp.d.mts +5 -0
- package/dist/extensions/mcp.d.mts.map +1 -0
- package/dist/extensions/mcp.mjs +21 -0
- package/dist/extensions/mcp.mjs.map +1 -0
- package/dist/extensions/server.cjs +611 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +606 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/hookModule-f-HwSbmi.d.mts +54 -0
- package/dist/hookModule-f-HwSbmi.d.mts.map +1 -0
- package/dist/hookModules-DSheklXp.cjs +899 -0
- package/dist/hookModules-DSheklXp.cjs.map +1 -0
- package/dist/hookModules-GoRDG8mg.mjs +749 -0
- package/dist/hookModules-GoRDG8mg.mjs.map +1 -0
- package/dist/index.cjs +23 -0
- package/dist/index.d.mts +362 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/llms.txt +11 -0
- package/package.json +122 -0
- package/src/prompts/po-author-hook/SKILL.md +128 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
const require_hookModules = require("../hookModules-DSheklXp.cjs");
|
|
6
|
+
let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
|
|
7
|
+
let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
|
|
8
|
+
let _agimon_ai_po_core_headless = require("@agimon-ai/po-core/headless");
|
|
9
|
+
let _agimon_ai_po_config_piContext = require("@agimon-ai/po-config/piContext");
|
|
10
|
+
let _agimon_ai_po_core_childSession = require("@agimon-ai/po-core/childSession");
|
|
11
|
+
let _agimon_ai_po_core_config = require("@agimon-ai/po-core/config");
|
|
12
|
+
let _agimon_ai_po_core_help = require("@agimon-ai/po-core/help");
|
|
13
|
+
//#region src/constants/hookHandlers.ts
|
|
14
|
+
const STATUS_PREFIX = "repository-hooks";
|
|
15
|
+
const FAILURE_MESSAGE_TYPE = "repository-hook-failure";
|
|
16
|
+
/** Claude's Stop hook input field: true when the agent is already continuing because of a Stop hook. */
|
|
17
|
+
const STOP_HOOK_ACTIVE_FIELD = "stop_hook_active";
|
|
18
|
+
const SUBAGENT_ENVIRONMENT_FLAG = "PI_SUBAGENT_CHILD";
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/services/hookDispatch/index.ts
|
|
21
|
+
function isJson(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
22
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
23
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
24
|
+
if (typeof value !== "object" || ancestors.has(value)) return false;
|
|
25
|
+
ancestors.add(value);
|
|
26
|
+
const valid = (Array.isArray(value) || Object.getPrototypeOf(value) === Object.prototype) && Object.values(value).every((entry) => isJson(entry, ancestors));
|
|
27
|
+
ancestors.delete(value);
|
|
28
|
+
return valid;
|
|
29
|
+
}
|
|
30
|
+
function commandFailure(row, error) {
|
|
31
|
+
return {
|
|
32
|
+
command: row.hook.command ?? row.hook.module,
|
|
33
|
+
message: error instanceof Error ? error.message : String(error),
|
|
34
|
+
reason: "hook_execution"
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Registry rows interleave command and module bindings, followed by plugin commands. */
|
|
38
|
+
async function dispatchHooks(session, scope, request) {
|
|
39
|
+
const signal = scope.operationSignal ? AbortSignal.any([scope.signal, scope.operationSignal]) : scope.signal;
|
|
40
|
+
signal.throwIfAborted();
|
|
41
|
+
const harness = session.config().harness;
|
|
42
|
+
const plugins = await session.documents.plugins(harness.pluginHooks);
|
|
43
|
+
const registry = request.eventName === require_hookModules.HOOK_EVENT.sessionEnd ? {
|
|
44
|
+
entries: [],
|
|
45
|
+
failure: void 0
|
|
46
|
+
} : await session.documents.registry(scope.repoRoot);
|
|
47
|
+
signal.throwIfAborted();
|
|
48
|
+
const event = request.event;
|
|
49
|
+
const toolName = event && "toolName" in event ? event.toolName : void 0;
|
|
50
|
+
const hooks = [...require_hookModules.selectRegistryHooks(registry.entries, {
|
|
51
|
+
event: request.eventName,
|
|
52
|
+
toolName,
|
|
53
|
+
allowedGroups: harness.hookGroups,
|
|
54
|
+
inSubagent: scope.isSubagent
|
|
55
|
+
}), ...require_hookModules.selectPluginHooks(plugins.documents, request.eventName, toolName)];
|
|
56
|
+
const decisions = [];
|
|
57
|
+
const failures = [...registry.failure ? [registry.failure] : [], ...plugins.failures];
|
|
58
|
+
let toolCall;
|
|
59
|
+
let patchedResult = false;
|
|
60
|
+
for (const [index, row] of hooks.entries()) {
|
|
61
|
+
signal.throwIfAborted();
|
|
62
|
+
request.progress?.(index, hooks.length);
|
|
63
|
+
if (row.hook.command === void 0) {
|
|
64
|
+
if (!event) continue;
|
|
65
|
+
let proposed;
|
|
66
|
+
try {
|
|
67
|
+
proposed = structuredClone(event);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
failures.push(commandFailure(row, error));
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const outcome = await session.modules.invoke(row, proposed, scope, scope.operationSignal);
|
|
73
|
+
signal.throwIfAborted();
|
|
74
|
+
if (outcome.failure) {
|
|
75
|
+
failures.push(outcome.failure);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
if (event.type === "tool_call" && proposed.type === "tool_call") {
|
|
80
|
+
if (typeof proposed.input !== "object" || proposed.input === null || Array.isArray(proposed.input) || !isJson(proposed.input)) throw new Error("Hook tool input must be a JSON object.");
|
|
81
|
+
const input = structuredClone(proposed.input);
|
|
82
|
+
for (const key of Object.keys(event.input)) delete event.input[key];
|
|
83
|
+
Object.assign(event.input, input);
|
|
84
|
+
if (outcome.result && "block" in outcome.result && outcome.result.block) {
|
|
85
|
+
toolCall = {
|
|
86
|
+
block: true,
|
|
87
|
+
reason: outcome.result.reason ?? "Blocked by repository hook"
|
|
88
|
+
};
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
} else if (event.type === "tool_result" && outcome.result) {
|
|
92
|
+
const patch = structuredClone(outcome.result);
|
|
93
|
+
if ("content" in patch && patch.content !== void 0) event.content = patch.content;
|
|
94
|
+
if ("details" in patch) event.details = patch.details;
|
|
95
|
+
if ("isError" in patch && patch.isError !== void 0) event.isError = patch.isError;
|
|
96
|
+
patchedResult = true;
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
failures.push(commandFailure(row, error));
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const payload = event && "toolName" in event ? require_hookModules.toolHookPayload(event, request.eventName, scope.repoRoot, scope.sessionId, {
|
|
104
|
+
parentSessionId: scope.parentSessionId,
|
|
105
|
+
agent: scope.agent
|
|
106
|
+
}) : require_hookModules.sessionHookPayload(scope.sessionId, scope.repoRoot);
|
|
107
|
+
let outcome;
|
|
108
|
+
try {
|
|
109
|
+
outcome = await session.runner.run(row.hook, {
|
|
110
|
+
...payload,
|
|
111
|
+
...request.extraPayload
|
|
112
|
+
}, {
|
|
113
|
+
repoRoot: scope.repoRoot,
|
|
114
|
+
pluginRoot: row.root
|
|
115
|
+
});
|
|
116
|
+
} catch (error) {
|
|
117
|
+
failures.push(commandFailure(row, error));
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
signal.throwIfAborted();
|
|
121
|
+
if (outcome.failure) failures.push(outcome.failure);
|
|
122
|
+
if (!outcome.decision) continue;
|
|
123
|
+
decisions.push(outcome.decision);
|
|
124
|
+
if (event?.type === "tool_result") {
|
|
125
|
+
const messages = require_hookModules.toolResultMessages([outcome.decision]);
|
|
126
|
+
if (messages.length > 0) event.content = [...event.content, {
|
|
127
|
+
type: "text",
|
|
128
|
+
text: messages.join("\n\n")
|
|
129
|
+
}];
|
|
130
|
+
if (require_hookModules.isDenied(outcome.decision)) event.isError = true;
|
|
131
|
+
patchedResult ||= messages.length > 0 || require_hookModules.isDenied(outcome.decision);
|
|
132
|
+
}
|
|
133
|
+
if (require_hookModules.isDenied(outcome.decision)) {
|
|
134
|
+
if (event?.type === "tool_call") toolCall = {
|
|
135
|
+
block: true,
|
|
136
|
+
reason: require_hookModules.decisionReason(outcome.decision) ?? "Blocked by repository hook"
|
|
137
|
+
};
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (event?.type === "tool_call" && !toolCall) {
|
|
142
|
+
const context = require_hookModules.additionalContextsFrom(decisions).join("\n\n");
|
|
143
|
+
if (context) toolCall = {
|
|
144
|
+
block: true,
|
|
145
|
+
reason: context
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (event?.type === "tool_result" && failures.length > 0) {
|
|
149
|
+
event.content = [...event.content, {
|
|
150
|
+
type: "text",
|
|
151
|
+
text: require_hookModules.hookFailureMessage(failures)
|
|
152
|
+
}];
|
|
153
|
+
patchedResult = true;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
decisions,
|
|
157
|
+
failures,
|
|
158
|
+
...toolCall ? { toolCall } : {},
|
|
159
|
+
...event?.type === "tool_result" && patchedResult ? { toolResult: {
|
|
160
|
+
content: event.content,
|
|
161
|
+
details: event.details,
|
|
162
|
+
isError: event.isError
|
|
163
|
+
} } : {}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/services/hookRuntime/type.ts
|
|
168
|
+
const DOOM_HOOK_SESSION_SERVICE = "doom/hook-session";
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/services/hookRuntime/index.ts
|
|
171
|
+
function createHookSession(config, options, signal) {
|
|
172
|
+
let telemetry = options.telemetry;
|
|
173
|
+
const requireTelemetry = () => telemetry ??= require_hookModules.createHookTelemetry();
|
|
174
|
+
const modules = require_hookModules.createHookModules({ descriptor: config().harness.hookModules });
|
|
175
|
+
const documents = options.documents ?? require_hookModules.createHookDocumentReader({ telemetry: requireTelemetry() });
|
|
176
|
+
let preparation;
|
|
177
|
+
return {
|
|
178
|
+
config,
|
|
179
|
+
signal,
|
|
180
|
+
modules,
|
|
181
|
+
prepare(isSubagent = false) {
|
|
182
|
+
preparation ??= (async () => {
|
|
183
|
+
const harness = config().harness;
|
|
184
|
+
if (harness.hooks === false) return;
|
|
185
|
+
const registry = await documents.registry(harness.root ?? process.cwd());
|
|
186
|
+
if (registry.failure) return;
|
|
187
|
+
const allowed = harness.hookGroups === void 0 ? void 0 : new Set(harness.hookGroups);
|
|
188
|
+
const rows = registry.entries.filter((row) => row.core || !allowed || allowed.has(row.groupId)).filter((row) => !(isSubagent && row.skipInSubagent));
|
|
189
|
+
await modules.validate(rows);
|
|
190
|
+
})();
|
|
191
|
+
return preparation;
|
|
192
|
+
},
|
|
193
|
+
runner: options.runner ?? require_hookModules.createBashHookRunner({ telemetry: requireTelemetry() }),
|
|
194
|
+
documents
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function childHooksFor(parent) {
|
|
198
|
+
return { bind(scope) {
|
|
199
|
+
const config = structuredClone(parent.config());
|
|
200
|
+
const controller = new AbortController();
|
|
201
|
+
const signal = AbortSignal.any([controller.signal, scope.signal]);
|
|
202
|
+
const session = createHookSession(() => config, {
|
|
203
|
+
runner: parent.runner,
|
|
204
|
+
documents: parent.documents
|
|
205
|
+
}, signal);
|
|
206
|
+
let disposal;
|
|
207
|
+
const context = () => {
|
|
208
|
+
const sessionId = scope.sessionId();
|
|
209
|
+
if (!sessionId) throw new Error("Child hook identity is unavailable before runtime startup.");
|
|
210
|
+
return {
|
|
211
|
+
sessionId,
|
|
212
|
+
parentSessionId: scope.request.parentSessionId,
|
|
213
|
+
agent: scope.request.agent,
|
|
214
|
+
isSubagent: true,
|
|
215
|
+
cwd: scope.request.cwd,
|
|
216
|
+
repoRoot: config.harness.root ?? scope.request.cwd,
|
|
217
|
+
signal,
|
|
218
|
+
sendMessage: (text, delivery) => scope.sendMessage(text, delivery),
|
|
219
|
+
appendCustomEntry: (type, data) => scope.appendCustomEntry(type, data)
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
return {
|
|
223
|
+
async beforeTool(event, execution) {
|
|
224
|
+
await session.prepare(true);
|
|
225
|
+
const native = {
|
|
226
|
+
type: "tool_call",
|
|
227
|
+
toolCallId: event.toolCallId,
|
|
228
|
+
toolName: event.toolName,
|
|
229
|
+
input: event.args
|
|
230
|
+
};
|
|
231
|
+
const result = await dispatchHooks(session, {
|
|
232
|
+
...context(),
|
|
233
|
+
operationSignal: execution.abortSignal
|
|
234
|
+
}, {
|
|
235
|
+
eventName: require_hookModules.HOOK_EVENT.preToolUse,
|
|
236
|
+
event: native
|
|
237
|
+
});
|
|
238
|
+
if (result.failures.length > 0) await scope.sendMessage(require_hookModules.hookFailureMessage(result.failures), "steer");
|
|
239
|
+
return result.toolCall?.block ? { block: { reason: result.toolCall.reason ?? "Blocked by repository hook" } } : { args: native.input };
|
|
240
|
+
},
|
|
241
|
+
async afterTool(event, execution) {
|
|
242
|
+
const native = {
|
|
243
|
+
type: "tool_result",
|
|
244
|
+
toolCallId: event.toolCallId,
|
|
245
|
+
toolName: event.toolName,
|
|
246
|
+
input: event.args,
|
|
247
|
+
content: event.content,
|
|
248
|
+
details: event.details,
|
|
249
|
+
isError: event.isError
|
|
250
|
+
};
|
|
251
|
+
const result = await dispatchHooks(session, {
|
|
252
|
+
...context(),
|
|
253
|
+
operationSignal: execution.abortSignal
|
|
254
|
+
}, {
|
|
255
|
+
eventName: require_hookModules.HOOK_EVENT.postToolUse,
|
|
256
|
+
event: native
|
|
257
|
+
});
|
|
258
|
+
return result.toolResult ? {
|
|
259
|
+
content: result.toolResult.content,
|
|
260
|
+
details: result.toolResult.details,
|
|
261
|
+
isError: result.toolResult.isError
|
|
262
|
+
} : void 0;
|
|
263
|
+
},
|
|
264
|
+
dispose() {
|
|
265
|
+
disposal ??= (async () => {
|
|
266
|
+
controller.abort();
|
|
267
|
+
await session.modules.dispose();
|
|
268
|
+
})();
|
|
269
|
+
return disposal;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
} };
|
|
273
|
+
}
|
|
274
|
+
function createHookRuntime(cordis, options, parentContext, lifetimeSignal) {
|
|
275
|
+
const controller = new AbortController();
|
|
276
|
+
const signal = lifetimeSignal ? AbortSignal.any([controller.signal, lifetimeSignal]) : controller.signal;
|
|
277
|
+
const session = {
|
|
278
|
+
...createHookSession(() => (0, _agimon_ai_po_config_piContext.requireDoomConfigContext)(cordis), options, signal),
|
|
279
|
+
parentContext
|
|
280
|
+
};
|
|
281
|
+
let disposal;
|
|
282
|
+
let active = true;
|
|
283
|
+
return {
|
|
284
|
+
session,
|
|
285
|
+
childHooks: childHooksFor(session),
|
|
286
|
+
isCurrent: () => active && !signal.aborted,
|
|
287
|
+
dispose() {
|
|
288
|
+
active = false;
|
|
289
|
+
controller.abort();
|
|
290
|
+
disposal ??= session.modules.dispose();
|
|
291
|
+
return disposal;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
function createHookBinding(options, parentContext, lifetimeSignal) {
|
|
296
|
+
let runtime;
|
|
297
|
+
return {
|
|
298
|
+
plugin(cordis) {
|
|
299
|
+
cordis.inject([_agimon_ai_po_core_config.DOOM_CONFIG_SERVICE], (configContext) => {
|
|
300
|
+
const binding = createHookRuntime(configContext, options, parentContext, lifetimeSignal);
|
|
301
|
+
runtime = binding;
|
|
302
|
+
configContext.provide(_agimon_ai_po_core_childSession.DOOM_CHILD_SESSION_HOOKS_SERVICE, binding.childHooks);
|
|
303
|
+
configContext.provide(DOOM_HOOK_SESSION_SERVICE, binding);
|
|
304
|
+
return async () => {
|
|
305
|
+
if (runtime === binding) runtime = void 0;
|
|
306
|
+
await binding.dispose();
|
|
307
|
+
};
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
runtime: () => runtime
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region src/services/serverHooks/index.ts
|
|
315
|
+
function hookEntry(event) {
|
|
316
|
+
return {
|
|
317
|
+
version: 1,
|
|
318
|
+
event: typeof event.type === "string" ? event.type : "headless-hook",
|
|
319
|
+
data: event
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
function scopeFor(runtime, execution) {
|
|
323
|
+
return {
|
|
324
|
+
sessionId: execution.sessionId,
|
|
325
|
+
isSubagent: Boolean(execution.environment[SUBAGENT_ENVIRONMENT_FLAG]),
|
|
326
|
+
cwd: execution.cwd,
|
|
327
|
+
repoRoot: execution.repoRoot,
|
|
328
|
+
model: execution.model,
|
|
329
|
+
signal: runtime.session.signal,
|
|
330
|
+
operationSignal: execution.signal,
|
|
331
|
+
async sendMessage(text, delivery) {
|
|
332
|
+
if (!execution.session.admitPrompt) throw new _agimon_ai_po_core_headless.DoomHeadlessPromptAdmissionError("The session cannot admit a hook prompt.");
|
|
333
|
+
await execution.session.admitPrompt(text, delivery);
|
|
334
|
+
},
|
|
335
|
+
appendCustomEntry: (type, data) => execution.session.appendCustomEntry(type, data)
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/** One native owner dispatches lifecycle and tool rows for this session. */
|
|
339
|
+
function createServerHooks(resolveRuntime) {
|
|
340
|
+
const startups = /* @__PURE__ */ new WeakMap();
|
|
341
|
+
const shutdowns = /* @__PURE__ */ new WeakMap();
|
|
342
|
+
const refusals = /* @__PURE__ */ new WeakMap();
|
|
343
|
+
async function runLifecycle(runtime, execution, eventName, event, extraPayload) {
|
|
344
|
+
const key = `${STATUS_PREFIX}:${execution.sessionId}:${eventName}`;
|
|
345
|
+
try {
|
|
346
|
+
return await dispatchHooks(runtime.session, scopeFor(runtime, execution), {
|
|
347
|
+
eventName,
|
|
348
|
+
event,
|
|
349
|
+
extraPayload,
|
|
350
|
+
progress: (index, total) => execution.client.setStatus(key, `Running ${eventName} hooks (${index + 1}/${total})...`)
|
|
351
|
+
});
|
|
352
|
+
} finally {
|
|
353
|
+
execution.client.setStatus(key, void 0);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function start(runtime, execution, event = { type: "session_start" }) {
|
|
357
|
+
let startup = startups.get(runtime);
|
|
358
|
+
if (!startup) {
|
|
359
|
+
startup = (async () => {
|
|
360
|
+
const scope = scopeFor(runtime, execution);
|
|
361
|
+
await runtime.session.prepare(scope.isSubagent);
|
|
362
|
+
if (scope.isSubagent) return;
|
|
363
|
+
const result = await runLifecycle(runtime, execution, require_hookModules.HOOK_EVENT.sessionStart, {
|
|
364
|
+
...event,
|
|
365
|
+
type: "session_start"
|
|
366
|
+
});
|
|
367
|
+
if (!runtime.isCurrent()) return;
|
|
368
|
+
if (result.failures.length) await scope.sendMessage(require_hookModules.hookFailureMessage(result.failures), "steer");
|
|
369
|
+
const context = require_hookModules.additionalContextsFrom(result.decisions).join("\n\n");
|
|
370
|
+
if (context) await scope.sendMessage(context, "steer");
|
|
371
|
+
})();
|
|
372
|
+
startups.set(runtime, startup);
|
|
373
|
+
}
|
|
374
|
+
return startup;
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
sessionStart: {
|
|
378
|
+
event: "session_start",
|
|
379
|
+
async handle(event, execution) {
|
|
380
|
+
const runtime = resolveRuntime();
|
|
381
|
+
if (runtime?.isCurrent()) await start(runtime, execution, event);
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
agentEnd: {
|
|
385
|
+
event: "agent_end",
|
|
386
|
+
async handle(event, execution) {
|
|
387
|
+
const runtime = resolveRuntime();
|
|
388
|
+
if (!runtime?.isCurrent() || scopeFor(runtime, execution).isSubagent) return;
|
|
389
|
+
await start(runtime, execution);
|
|
390
|
+
const count = refusals.get(runtime) ?? 0;
|
|
391
|
+
const result = await runLifecycle(runtime, execution, require_hookModules.HOOK_EVENT.stop, {
|
|
392
|
+
...event,
|
|
393
|
+
type: "agent_settled"
|
|
394
|
+
}, { [STOP_HOOK_ACTIVE_FIELD]: count > 0 });
|
|
395
|
+
if (!runtime.isCurrent()) return;
|
|
396
|
+
const appendFailure = async (text) => {
|
|
397
|
+
if (!execution.session.appendMessage) throw new Error("The session cannot append hook diagnostics.");
|
|
398
|
+
await execution.session.appendMessage({
|
|
399
|
+
type: FAILURE_MESSAGE_TYPE,
|
|
400
|
+
text
|
|
401
|
+
});
|
|
402
|
+
};
|
|
403
|
+
if (result.failures.length) await appendFailure(require_hookModules.hookFailureMessage(result.failures));
|
|
404
|
+
const reason = require_hookModules.decisionReason(result.decisions.find(require_hookModules.isDenied));
|
|
405
|
+
if (!reason) {
|
|
406
|
+
refusals.set(runtime, 0);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (count >= 5) {
|
|
410
|
+
refusals.set(runtime, 0);
|
|
411
|
+
await appendFailure(`A Stop hook refused ${String(5)} stops in a row, so the agent stops here. Its last reason: ${reason}`);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
await scopeFor(runtime, execution).sendMessage(reason, "followUp");
|
|
415
|
+
refusals.set(runtime, count + 1);
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
beforeAgentStart: {
|
|
419
|
+
event: "before_agent_start",
|
|
420
|
+
async handle(event, execution) {
|
|
421
|
+
const runtime = resolveRuntime();
|
|
422
|
+
if (runtime?.isCurrent()) await start(runtime, execution);
|
|
423
|
+
await execution.session.appendCustomEntry("doom-hook", hookEntry(event));
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
toolCall: {
|
|
427
|
+
event: "tool_call",
|
|
428
|
+
async handle(event, execution) {
|
|
429
|
+
const runtime = resolveRuntime();
|
|
430
|
+
if (!runtime?.isCurrent()) return;
|
|
431
|
+
const scope = scopeFor(runtime, execution);
|
|
432
|
+
try {
|
|
433
|
+
await start(runtime, execution);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
return { block: { reason: error instanceof Error ? error.message : String(error) } };
|
|
436
|
+
}
|
|
437
|
+
const native = {
|
|
438
|
+
type: "tool_call",
|
|
439
|
+
toolCallId: event.toolCallId,
|
|
440
|
+
toolName: event.toolName,
|
|
441
|
+
input: event.args
|
|
442
|
+
};
|
|
443
|
+
const key = `${STATUS_PREFIX}:${native.toolCallId}:pre`;
|
|
444
|
+
try {
|
|
445
|
+
const result = await dispatchHooks(runtime.session, scope, {
|
|
446
|
+
eventName: require_hookModules.HOOK_EVENT.preToolUse,
|
|
447
|
+
event: native,
|
|
448
|
+
progress: (index, total) => execution.client.setStatus(key, `Running pre-tool hooks for ${native.toolName} (${index + 1}/${total})...`)
|
|
449
|
+
});
|
|
450
|
+
if (result.failures.length > 0) await scope.sendMessage(require_hookModules.hookFailureMessage(result.failures), "steer");
|
|
451
|
+
return result.toolCall?.block ? { block: { reason: result.toolCall.reason ?? "Blocked by repository hook" } } : { args: native.input };
|
|
452
|
+
} finally {
|
|
453
|
+
execution.client.setStatus(key, void 0);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
toolResult: {
|
|
458
|
+
event: "tool_result",
|
|
459
|
+
async handle(event, execution) {
|
|
460
|
+
await execution.session.appendCustomEntry("doom-hook", hookEntry(event));
|
|
461
|
+
const runtime = resolveRuntime();
|
|
462
|
+
if (!runtime?.isCurrent()) return;
|
|
463
|
+
await start(runtime, execution);
|
|
464
|
+
const native = {
|
|
465
|
+
type: "tool_result",
|
|
466
|
+
toolCallId: event.toolCallId,
|
|
467
|
+
toolName: event.toolName,
|
|
468
|
+
input: event.args,
|
|
469
|
+
content: event.content,
|
|
470
|
+
details: event.details,
|
|
471
|
+
isError: event.isError
|
|
472
|
+
};
|
|
473
|
+
const key = `${STATUS_PREFIX}:${native.toolCallId}:post`;
|
|
474
|
+
try {
|
|
475
|
+
return (await dispatchHooks(runtime.session, scopeFor(runtime, execution), {
|
|
476
|
+
eventName: require_hookModules.HOOK_EVENT.postToolUse,
|
|
477
|
+
event: native,
|
|
478
|
+
progress: (index, total) => execution.client.setStatus(key, `Running post-tool hooks for ${native.toolName} (${index + 1}/${total})...`)
|
|
479
|
+
})).toolResult;
|
|
480
|
+
} finally {
|
|
481
|
+
execution.client.setStatus(key, void 0);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
},
|
|
485
|
+
agentSettled: {
|
|
486
|
+
event: "agent_settled",
|
|
487
|
+
handle: (event, execution) => execution.session.appendCustomEntry("doom-hook", hookEntry(event))
|
|
488
|
+
},
|
|
489
|
+
sessionShutdown: {
|
|
490
|
+
event: "session_shutdown",
|
|
491
|
+
async handle(_event, execution) {
|
|
492
|
+
const runtime = resolveRuntime();
|
|
493
|
+
if (runtime?.isCurrent() && !scopeFor(runtime, execution).isSubagent) {
|
|
494
|
+
let shutdown = shutdowns.get(runtime);
|
|
495
|
+
if (!shutdown) {
|
|
496
|
+
shutdown = runLifecycle(runtime, execution, require_hookModules.HOOK_EVENT.sessionEnd).then(() => void 0);
|
|
497
|
+
shutdowns.set(runtime, shutdown);
|
|
498
|
+
}
|
|
499
|
+
await shutdown;
|
|
500
|
+
}
|
|
501
|
+
execution.client.setStatus("doom-hook", void 0);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/extensions/workspaces/sessions/(backend)/_lib/serverRoot.ts
|
|
508
|
+
const createHookServerRoot = ({ agent, signal }) => {
|
|
509
|
+
if (!agent) throw new Error("Hook session requires the headless host.");
|
|
510
|
+
const execution = agent.context;
|
|
511
|
+
const binding = createHookBinding({}, {
|
|
512
|
+
sessionId: execution.sessionId,
|
|
513
|
+
isSubagent: Boolean(execution.environment[SUBAGENT_ENVIRONMENT_FLAG]),
|
|
514
|
+
cwd: execution.cwd,
|
|
515
|
+
repoRoot: execution.repoRoot,
|
|
516
|
+
get model() {
|
|
517
|
+
return execution.model;
|
|
518
|
+
},
|
|
519
|
+
async sendMessage(text, delivery) {
|
|
520
|
+
if (!execution.session.admitPrompt) throw new _agimon_ai_po_core_headless.DoomHeadlessPromptAdmissionError("The session cannot admit a hook prompt.");
|
|
521
|
+
await execution.session.admitPrompt(text, delivery);
|
|
522
|
+
},
|
|
523
|
+
appendCustomEntry: (type, data) => execution.session.appendCustomEntry(type, data)
|
|
524
|
+
}, signal);
|
|
525
|
+
return {
|
|
526
|
+
value: createServerHooks(binding.runtime),
|
|
527
|
+
services: [binding.plugin]
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/extensions/workspaces/sessions/(backend)/root.server.ts
|
|
532
|
+
var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)(createHookServerRoot);
|
|
533
|
+
//#endregion
|
|
534
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/agent-end.server.ts
|
|
535
|
+
var agent_end_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)(({ root }) => root.agentEnd);
|
|
536
|
+
//#endregion
|
|
537
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts
|
|
538
|
+
var agent_settled_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)(({ root }) => root.agentSettled);
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts
|
|
541
|
+
var before_agent_start_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)(({ root }) => root.beforeAgentStart);
|
|
542
|
+
//#endregion
|
|
543
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
|
|
544
|
+
var session_shutdown_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)(({ root }) => root.sessionShutdown);
|
|
545
|
+
//#endregion
|
|
546
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts
|
|
547
|
+
var session_start_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)(({ root }) => root.sessionStart);
|
|
548
|
+
//#endregion
|
|
549
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/tool-call.server.ts
|
|
550
|
+
var tool_call_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)(({ root }) => root.toolCall);
|
|
551
|
+
//#endregion
|
|
552
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/tool-result.server.ts
|
|
553
|
+
var tool_result_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)(({ root }) => root.toolResult);
|
|
554
|
+
//#endregion
|
|
555
|
+
//#region src/extensions/workspaces/sessions/(backend)/resource/po-author-hook.server.ts
|
|
556
|
+
var po_author_hook_server_default = (0, _agimon_ai_po_core_extensionFile.defineResource)({
|
|
557
|
+
name: "po-author-hook",
|
|
558
|
+
description: "Author Po repository or plugin hooks. Use when creating or changing .po/hooks.yaml, selecting hook groups from modes.yaml, writing hook commands, or adapting Claude Code hook payloads and decisions to Po.",
|
|
559
|
+
when: _agimon_ai_po_core_help.DOOM_HELP_WHEN,
|
|
560
|
+
kind: "skill",
|
|
561
|
+
path: (0, _agimon_ai_po_core_serverFacet.packageResourcePath)(require("url").pathToFileURL(__filename).href, "src/prompts/po-author-hook/SKILL.md"),
|
|
562
|
+
read: async () => {
|
|
563
|
+
const text = await (0, _agimon_ai_po_core_serverFacet.readPackageResource)(require("url").pathToFileURL(__filename).href, "src/prompts/po-author-hook/SKILL.md");
|
|
564
|
+
if (!text.trim()) throw new Error("The installed hook-authoring skill is missing or unreadable.");
|
|
565
|
+
return text;
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
//#endregion
|
|
569
|
+
//#region generated/server.ts
|
|
570
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
571
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
572
|
+
...identity,
|
|
573
|
+
...value
|
|
574
|
+
};
|
|
575
|
+
const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
|
|
576
|
+
name: "@agimon-ai/po-hook",
|
|
577
|
+
providerOverrides: (0, _agimon_ai_po_core_serverFacet.composeServerProviderOverrides)(root_server_default),
|
|
578
|
+
session: async (context) => {
|
|
579
|
+
const scopeSession = await root_server_default(context);
|
|
580
|
+
const contextSession = {
|
|
581
|
+
...context,
|
|
582
|
+
root: scopeSession.value
|
|
583
|
+
};
|
|
584
|
+
return {
|
|
585
|
+
...(0, _agimon_ai_po_core_extensionFile.composeRootHooks)(scopeSession),
|
|
586
|
+
get hooks() {
|
|
587
|
+
return [
|
|
588
|
+
via({ event: "agent_end" }, at(agent_end_server_default, contextSession)),
|
|
589
|
+
via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)),
|
|
590
|
+
via({ event: "before_agent_start" }, at(before_agent_start_server_default, contextSession)),
|
|
591
|
+
via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession)),
|
|
592
|
+
via({ event: "session_start" }, at(session_start_server_default, contextSession)),
|
|
593
|
+
via({ event: "tool_call" }, at(tool_call_server_default, contextSession)),
|
|
594
|
+
via({ event: "tool_result" }, at(tool_result_server_default, contextSession))
|
|
595
|
+
];
|
|
596
|
+
},
|
|
597
|
+
get resources() {
|
|
598
|
+
return [at(po_author_hook_server_default, contextSession)];
|
|
599
|
+
},
|
|
600
|
+
services: [...scopeSession.services ?? []],
|
|
601
|
+
get activities() {
|
|
602
|
+
return [...scopeSession.activities ?? []];
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
//#endregion
|
|
608
|
+
exports.default = facet;
|
|
609
|
+
exports.facet = facet;
|
|
610
|
+
|
|
611
|
+
//# sourceMappingURL=server.cjs.map
|