@agimon-ai/po-autostop 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 +37 -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/extensions/server.cjs +287 -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 +282 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/idlePolicy-BNLdwumq.mjs +60 -0
- package/dist/idlePolicy-BNLdwumq.mjs.map +1 -0
- package/dist/idlePolicy-Dw8blgEH.cjs +101 -0
- package/dist/idlePolicy-Dw8blgEH.cjs.map +1 -0
- package/dist/index.cjs +6 -0
- package/dist/index.d.mts +56 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +2 -0
- package/llms.txt +7 -0
- package/package.json +105 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vuong Ngo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @agimon-ai/po-autostop
|
|
2
|
+
|
|
3
|
+
Shuts a [Po](https://www.npmjs.com/package/@agimon-ai/po) session down once the agent
|
|
4
|
+
settles and stays idle.
|
|
5
|
+
|
|
6
|
+
A scripted run wants the session to exit on its own when the work is done. Waiting for
|
|
7
|
+
`agent_settled` alone is not enough: the agent can settle and immediately pick up a queued
|
|
8
|
+
message, and it can settle while the last response is still streaming. So this package settles,
|
|
9
|
+
waits, looks again, and only then stops.
|
|
10
|
+
|
|
11
|
+
## The policy
|
|
12
|
+
|
|
13
|
+
| Moment | Session state | Outcome |
|
|
14
|
+
| ------------------ | --------------------------------- | -------------------- |
|
|
15
|
+
| `agent_settled` | messages queued | stand down |
|
|
16
|
+
| `agent_settled` | queue empty | look again in 5s |
|
|
17
|
+
| the scheduled look | messages queued | stand down |
|
|
18
|
+
| the scheduled look | background runner or agent active | look again in 5s |
|
|
19
|
+
| the scheduled look | still streaming | look again in 100ms |
|
|
20
|
+
| the scheduled look | idle, queue and background empty | `context.shutdown()` |
|
|
21
|
+
|
|
22
|
+
Any `input` or `agent_start` event disarms a pending stop. The cooldown is the grace period;
|
|
23
|
+
the 100 ms recheck waits out a stream that has not finished draining. Only two kinds of
|
|
24
|
+
background work keep a settled session alive: supervised Runner processes (`doom-runner`)
|
|
25
|
+
while running or awaiting asynchronous completion admission (published without a status).
|
|
26
|
+
A runner listed as `completed` or `failed` no longer counts. Owned subagents (`team-direct-runs`, `doom-task`) owned by the current session, including
|
|
27
|
+
provider errors from them. The holding rule is shared with workflow step supervision through core's
|
|
28
|
+
`holdsSettledSession`. Workflow runs (`workflow-mcp`) do not hold a session open.
|
|
29
|
+
|
|
30
|
+
Each wait is recorded as `doom_autostop.waiting_on_background_work` telemetry naming the work
|
|
31
|
+
holding the session, along with `doom_autostop.stood_down` and `doom_autostop.shutdown_requested`.
|
|
32
|
+
|
|
33
|
+
The idle shutdown watch adds the authoritative background-work gate before the policy may stop the session.
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
//#region src/schemas/apiContracts.ts
|
|
6
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
7
|
+
const apiContracts = (0, require("@agimon-ai/po-core/apiContracts").defineApiContract)({
|
|
8
|
+
version: 1,
|
|
9
|
+
http: [],
|
|
10
|
+
sockets: [],
|
|
11
|
+
dynamic: []
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
exports.apiContracts = apiContracts;
|
|
15
|
+
exports.default = apiContracts;
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=apiContracts.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.cjs","names":["defineApiContract"],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/po-core/apiContracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;;;;AAGA,MAAa,gBAAA,4CAAeA,CAAAA,CAAAA,kBAAAA,CAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/schemas/apiContracts.d.ts
|
|
2
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
3
|
+
export declare const apiContracts: {
|
|
4
|
+
version: 1;
|
|
5
|
+
protocols?: Record<string, string> | undefined;
|
|
6
|
+
schemas?: Record<string, boolean | object> | undefined;
|
|
7
|
+
http: {
|
|
8
|
+
id: string;
|
|
9
|
+
scope: "global" | "session" | "workspace";
|
|
10
|
+
basePath?: string | undefined;
|
|
11
|
+
path: string;
|
|
12
|
+
method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
|
|
13
|
+
description: string;
|
|
14
|
+
availability?: string | undefined;
|
|
15
|
+
authentication: "device" | "none" | "owner";
|
|
16
|
+
parameters?: {
|
|
17
|
+
name: string;
|
|
18
|
+
in: "header" | "path" | "query";
|
|
19
|
+
required: boolean;
|
|
20
|
+
schema: boolean | object;
|
|
21
|
+
}[] | undefined;
|
|
22
|
+
body?: {
|
|
23
|
+
contentType: string;
|
|
24
|
+
contentTypes?: string[] | undefined;
|
|
25
|
+
required: boolean;
|
|
26
|
+
schema: boolean | object;
|
|
27
|
+
} | undefined;
|
|
28
|
+
responses: Record<string, {
|
|
29
|
+
description: string;
|
|
30
|
+
contentType?: string | undefined;
|
|
31
|
+
schema?: boolean | object | undefined;
|
|
32
|
+
events?: Record<string, boolean | object> | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
}[];
|
|
35
|
+
sockets: {
|
|
36
|
+
id: string;
|
|
37
|
+
scope: "global" | "session" | "workspace";
|
|
38
|
+
description: string;
|
|
39
|
+
availability?: string | undefined;
|
|
40
|
+
service: string;
|
|
41
|
+
member: string;
|
|
42
|
+
direction: "client-to-server" | "server-to-client";
|
|
43
|
+
kind: "channel" | "method" | "state";
|
|
44
|
+
input: boolean | object;
|
|
45
|
+
output?: boolean | object | undefined;
|
|
46
|
+
errors?: boolean | object | undefined;
|
|
47
|
+
}[];
|
|
48
|
+
dynamic: string[];
|
|
49
|
+
gaps?: string[] | undefined;
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
export { apiContracts as default };
|
|
53
|
+
//# sourceMappingURL=apiContracts.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineApiContract } from "@agimon-ai/po-core/apiContracts";
|
|
2
|
+
//#region src/schemas/apiContracts.ts
|
|
3
|
+
/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
|
|
4
|
+
const apiContracts = defineApiContract({
|
|
5
|
+
version: 1,
|
|
6
|
+
http: [],
|
|
7
|
+
sockets: [],
|
|
8
|
+
dynamic: []
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
11
|
+
export { apiContracts, apiContracts as default };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=apiContracts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiContracts.mjs","names":[],"sources":["../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/po-core/apiContracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":";;;AAGA,MAAa,eAAe,kBAAkB;CAAE,SAAS;CAAG,MAAM,CAAC;CAAG,SAAS,CAAC;CAAG,SAAS,CAAC;AAAE,CAAC"}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
const require_idlePolicy = require("../idlePolicy-Dw8blgEH.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_telemetry = require("@agimon-ai/po-telemetry");
|
|
9
|
+
let _agimon_ai_po_core_backgroundWork = require("@agimon-ai/po-core/backgroundWork");
|
|
10
|
+
//#region src/services/autostopDiagnostics/index.ts
|
|
11
|
+
function detail(values) {
|
|
12
|
+
const joined = values.join(", ");
|
|
13
|
+
return joined.length > 500 ? `${joined.slice(0, 499)}…` : joined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Report what an idle session is waiting on before it stops.
|
|
17
|
+
*
|
|
18
|
+
* The first wait is reported at once and a long one again each interval, so a
|
|
19
|
+
* session held open for an hour names the work holding it without flooding
|
|
20
|
+
* the sink every cooldown.
|
|
21
|
+
*/
|
|
22
|
+
function createAutostopDiagnostics(options) {
|
|
23
|
+
const reportIntervalMs = options.reportIntervalMs ?? 6e4;
|
|
24
|
+
const flushTimeoutMs = options.flushTimeoutMs ?? 2e3;
|
|
25
|
+
let waitStartedAt;
|
|
26
|
+
let lastReportedAt;
|
|
27
|
+
let deferrals = 0;
|
|
28
|
+
const record = (event, attributes) => {
|
|
29
|
+
options.telemetry.recordEvent(event, attributes).catch(() => void 0);
|
|
30
|
+
};
|
|
31
|
+
const waitAttributes = (now) => ({
|
|
32
|
+
deferral_count: deferrals,
|
|
33
|
+
waited_ms: waitStartedAt === void 0 ? 0 : now - waitStartedAt
|
|
34
|
+
});
|
|
35
|
+
const reset = () => {
|
|
36
|
+
waitStartedAt = void 0;
|
|
37
|
+
lastReportedAt = void 0;
|
|
38
|
+
deferrals = 0;
|
|
39
|
+
};
|
|
40
|
+
return {
|
|
41
|
+
waiting(work) {
|
|
42
|
+
const now = options.now();
|
|
43
|
+
waitStartedAt ??= now;
|
|
44
|
+
deferrals += 1;
|
|
45
|
+
if (lastReportedAt !== void 0 && now - lastReportedAt < reportIntervalMs) return;
|
|
46
|
+
lastReportedAt = now;
|
|
47
|
+
record(require_idlePolicy.AUTO_STOP_EVENT.waitingOnBackgroundWork, {
|
|
48
|
+
...waitAttributes(now),
|
|
49
|
+
item_count: work.items.length,
|
|
50
|
+
error_count: work.errors.length,
|
|
51
|
+
items: detail(work.items),
|
|
52
|
+
errors: detail(work.errors)
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
stoodDown() {
|
|
56
|
+
record(require_idlePolicy.AUTO_STOP_EVENT.stoodDown, {
|
|
57
|
+
...waitAttributes(options.now()),
|
|
58
|
+
reason: "pending_messages"
|
|
59
|
+
});
|
|
60
|
+
reset();
|
|
61
|
+
},
|
|
62
|
+
shutdownRequested() {
|
|
63
|
+
record(require_idlePolicy.AUTO_STOP_EVENT.shutdownRequested, waitAttributes(options.now()));
|
|
64
|
+
reset();
|
|
65
|
+
},
|
|
66
|
+
reset,
|
|
67
|
+
async dispose() {
|
|
68
|
+
let timer;
|
|
69
|
+
await Promise.race([options.telemetry.shutdown().catch(() => void 0), new Promise((settle) => {
|
|
70
|
+
timer = setTimeout(settle, flushTimeoutMs);
|
|
71
|
+
timer.unref?.();
|
|
72
|
+
})]);
|
|
73
|
+
if (timer) clearTimeout(timer);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/services/backgroundWorkGate/index.ts
|
|
79
|
+
const NO_WORK$1 = Object.freeze({
|
|
80
|
+
active: false,
|
|
81
|
+
items: [],
|
|
82
|
+
errors: []
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Tracks the current background-work coordinator. Only runners and subagents
|
|
86
|
+
* count, and it fails closed when their snapshot is uncertain.
|
|
87
|
+
*/
|
|
88
|
+
function createBackgroundWorkGate() {
|
|
89
|
+
let backgroundWork;
|
|
90
|
+
const inspect = (sessionId) => {
|
|
91
|
+
if (!backgroundWork) return NO_WORK$1;
|
|
92
|
+
try {
|
|
93
|
+
const snapshot = backgroundWork.snapshot(sessionId);
|
|
94
|
+
const items = snapshot.items.filter(_agimon_ai_po_core_backgroundWork.holdsSettledSession).map((item) => `${item.provider}:${item.id}`);
|
|
95
|
+
const errors = snapshot.errors.filter((error) => _agimon_ai_po_core_backgroundWork.BACKGROUND_WORK_HOLDING_PROVIDERS.includes(error.provider)).map((error) => `${error.provider}: ${error.message}`);
|
|
96
|
+
return {
|
|
97
|
+
active: items.length > 0 || errors.length > 0,
|
|
98
|
+
items,
|
|
99
|
+
errors
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
active: true,
|
|
104
|
+
items: [],
|
|
105
|
+
errors: [`snapshot: ${error instanceof Error ? error.message : String(error)}`]
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
plugin: (context) => {
|
|
111
|
+
context.inject([_agimon_ai_po_core_backgroundWork.DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {
|
|
112
|
+
const service = (0, _agimon_ai_po_core_backgroundWork.readDoomBackgroundWorkService)(serviceContext);
|
|
113
|
+
if (!service) return void 0;
|
|
114
|
+
backgroundWork = service;
|
|
115
|
+
return () => {
|
|
116
|
+
if (backgroundWork === service) backgroundWork = void 0;
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
hasActiveWork: (sessionId) => inspect(sessionId).active,
|
|
121
|
+
inspect
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/services/serverIdleShutdown/index.ts
|
|
126
|
+
const NO_WORK = Object.freeze({
|
|
127
|
+
active: false,
|
|
128
|
+
items: [],
|
|
129
|
+
errors: []
|
|
130
|
+
});
|
|
131
|
+
function createServerIdleShutdown(host, inspectBackgroundWork = () => NO_WORK, delays = require_idlePolicy.DEFAULT_AUTO_STOP_DELAYS, diagnostics) {
|
|
132
|
+
let timer;
|
|
133
|
+
let watching = false;
|
|
134
|
+
let disposed = false;
|
|
135
|
+
let generation = 0;
|
|
136
|
+
const stopWatching = () => {
|
|
137
|
+
generation += 1;
|
|
138
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
139
|
+
timer = void 0;
|
|
140
|
+
watching = false;
|
|
141
|
+
diagnostics?.reset();
|
|
142
|
+
};
|
|
143
|
+
const schedule = (delayMs) => {
|
|
144
|
+
if (disposed) return;
|
|
145
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
146
|
+
watching = true;
|
|
147
|
+
const expected = ++generation;
|
|
148
|
+
timer = setTimeout(() => {
|
|
149
|
+
timer = void 0;
|
|
150
|
+
recheck(expected).catch(() => {
|
|
151
|
+
if (expected === generation) stopWatching();
|
|
152
|
+
});
|
|
153
|
+
}, delayMs);
|
|
154
|
+
};
|
|
155
|
+
const recheck = async (expected) => {
|
|
156
|
+
if (disposed || !watching || expected !== generation) return;
|
|
157
|
+
const work = inspectBackgroundWork();
|
|
158
|
+
if (work.active) {
|
|
159
|
+
diagnostics?.waiting(work);
|
|
160
|
+
schedule(delays.cooldownMs);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const activity = await host.context.session.activity();
|
|
164
|
+
if (disposed || !watching || expected !== generation) return;
|
|
165
|
+
const currentWork = inspectBackgroundWork();
|
|
166
|
+
if (currentWork.active) {
|
|
167
|
+
diagnostics?.waiting(currentWork);
|
|
168
|
+
schedule(delays.cooldownMs);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const decision = require_idlePolicy.decideOnRecheck(activity, delays);
|
|
172
|
+
if (decision.action === "shutdown") {
|
|
173
|
+
diagnostics?.shutdownRequested();
|
|
174
|
+
stopWatching();
|
|
175
|
+
host.context.shutdown();
|
|
176
|
+
} else if (decision.action === "stand-down") {
|
|
177
|
+
diagnostics?.stoodDown();
|
|
178
|
+
stopWatching();
|
|
179
|
+
} else schedule(decision.delayMs);
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
hooks: [
|
|
183
|
+
{
|
|
184
|
+
event: "agent_start",
|
|
185
|
+
handle: stopWatching
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
event: "agent_settled",
|
|
189
|
+
async handle() {
|
|
190
|
+
stopWatching();
|
|
191
|
+
const expected = generation;
|
|
192
|
+
const activity = await host.context.session.activity();
|
|
193
|
+
if (disposed || expected !== generation) return;
|
|
194
|
+
const decision = require_idlePolicy.decideOnSettled(activity, delays);
|
|
195
|
+
if (decision.action === "recheck") schedule(decision.delayMs);
|
|
196
|
+
else diagnostics?.stoodDown();
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
event: "session_shutdown",
|
|
201
|
+
handle: stopWatching
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
event: "session_tree",
|
|
205
|
+
handle: () => recheck(generation)
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
dispose() {
|
|
209
|
+
disposed = true;
|
|
210
|
+
stopWatching();
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
//#endregion
|
|
215
|
+
//#region src/extensions/workspaces/sessions/(backend)/root.server.ts
|
|
216
|
+
var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)(({ agent }) => {
|
|
217
|
+
if (!agent) throw new Error("Autostop requires a session host.");
|
|
218
|
+
const backgroundWork = createBackgroundWorkGate();
|
|
219
|
+
const diagnostics = createAutostopDiagnostics({
|
|
220
|
+
telemetry: (0, _agimon_ai_po_telemetry.createDoomTelemetry)({
|
|
221
|
+
serviceName: "doom-autostop",
|
|
222
|
+
packageName: "@agimon-ai/po-autostop",
|
|
223
|
+
enableLogs: true,
|
|
224
|
+
enableTraces: false
|
|
225
|
+
}),
|
|
226
|
+
now: Date.now
|
|
227
|
+
});
|
|
228
|
+
const watch = createServerIdleShutdown(agent, () => backgroundWork.inspect(agent.context.sessionId), void 0, diagnostics);
|
|
229
|
+
return {
|
|
230
|
+
value: watch,
|
|
231
|
+
services: [backgroundWork.plugin],
|
|
232
|
+
onDispose: async () => {
|
|
233
|
+
watch.dispose();
|
|
234
|
+
await diagnostics.dispose();
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
});
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts
|
|
240
|
+
var agent_settled_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)((context) => context.root.hooks.find((hook) => hook.event === "agent_settled"));
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/agent-start.server.ts
|
|
243
|
+
var agent_start_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)((context) => context.root.hooks.find((hook) => hook.event === "agent_start"));
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
|
|
246
|
+
var session_shutdown_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)((context) => context.root.hooks.find((hook) => hook.event === "session_shutdown"));
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-tree.server.ts
|
|
249
|
+
var session_tree_server_default = (0, _agimon_ai_po_core_extensionFile.defineHook)((context) => context.root.hooks.find((hook) => hook.event === "session_tree"));
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region generated/server.ts
|
|
252
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
253
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
254
|
+
...identity,
|
|
255
|
+
...value
|
|
256
|
+
};
|
|
257
|
+
const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
|
|
258
|
+
name: "@agimon-ai/po-autostop",
|
|
259
|
+
providerOverrides: (0, _agimon_ai_po_core_serverFacet.composeServerProviderOverrides)(root_server_default),
|
|
260
|
+
session: async (context) => {
|
|
261
|
+
const scopeSession = await root_server_default(context);
|
|
262
|
+
const contextSession = {
|
|
263
|
+
...context,
|
|
264
|
+
root: scopeSession.value
|
|
265
|
+
};
|
|
266
|
+
return {
|
|
267
|
+
...(0, _agimon_ai_po_core_extensionFile.composeRootHooks)(scopeSession),
|
|
268
|
+
get hooks() {
|
|
269
|
+
return [
|
|
270
|
+
via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)),
|
|
271
|
+
via({ event: "agent_start" }, at(agent_start_server_default, contextSession)),
|
|
272
|
+
via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession)),
|
|
273
|
+
via({ event: "session_tree" }, at(session_tree_server_default, contextSession))
|
|
274
|
+
];
|
|
275
|
+
},
|
|
276
|
+
services: [...scopeSession.services ?? []],
|
|
277
|
+
get activities() {
|
|
278
|
+
return [...scopeSession.activities ?? []];
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
//#endregion
|
|
284
|
+
exports.default = facet;
|
|
285
|
+
exports.facet = facet;
|
|
286
|
+
|
|
287
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.cjs","names":["AUTO_STOP_EVENT","NO_WORK","holdsSettledSession","BACKGROUND_WORK_HOLDING_PROVIDERS","DOOM_BACKGROUND_WORK_SERVICE","readDoomBackgroundWorkService","DEFAULT_AUTO_STOP_DELAYS","decideOnRecheck","decideOnSettled","defineRoot","createDoomTelemetry","defineHook","defineHook","defineHook","defineHook","defineServerPlugin","composeServerProviderOverrides","rootSession","composeRootHooks","hookAgentSettled","hookAgentStart","hookSessionShutdown","hookSessionTree"],"sources":["../../src/services/autostopDiagnostics/index.ts","../../src/services/backgroundWorkGate/index.ts","../../src/services/serverIdleShutdown/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-tree.server.ts","../../generated/server.ts"],"sourcesContent":["import {\n AUTO_STOP_DETAIL_MAX_LENGTH,\n AUTO_STOP_EVENT,\n AUTO_STOP_REPORT_INTERVAL_MS,\n AUTO_STOP_TELEMETRY_FLUSH_MS,\n} from '../../constants/idlePolicy';\nimport type { AutostopDiagnostics, AutostopDiagnosticsOptions, AutostopEventAttributes } from './type';\n\nfunction detail(values: readonly string[]): string {\n const joined = values.join(', ');\n return joined.length > AUTO_STOP_DETAIL_MAX_LENGTH ? `${joined.slice(0, AUTO_STOP_DETAIL_MAX_LENGTH - 1)}…` : joined;\n}\n\n/**\n * Report what an idle session is waiting on before it stops.\n *\n * The first wait is reported at once and a long one again each interval, so a\n * session held open for an hour names the work holding it without flooding\n * the sink every cooldown.\n */\nexport function createAutostopDiagnostics(options: AutostopDiagnosticsOptions): AutostopDiagnostics {\n const reportIntervalMs = options.reportIntervalMs ?? AUTO_STOP_REPORT_INTERVAL_MS;\n const flushTimeoutMs = options.flushTimeoutMs ?? AUTO_STOP_TELEMETRY_FLUSH_MS;\n let waitStartedAt: number | undefined;\n let lastReportedAt: number | undefined;\n let deferrals = 0;\n\n const record = (event: string, attributes: AutostopEventAttributes): void => {\n // Diagnostics must never change whether the session stops.\n options.telemetry.recordEvent(event, attributes).catch(() => undefined);\n };\n const waitAttributes = (now: number): AutostopEventAttributes => ({\n deferral_count: deferrals,\n waited_ms: waitStartedAt === undefined ? 0 : now - waitStartedAt,\n });\n const reset = (): void => {\n waitStartedAt = undefined;\n lastReportedAt = undefined;\n deferrals = 0;\n };\n\n return {\n waiting(work) {\n const now = options.now();\n waitStartedAt ??= now;\n deferrals += 1;\n if (lastReportedAt !== undefined && now - lastReportedAt < reportIntervalMs) return;\n lastReportedAt = now;\n record(AUTO_STOP_EVENT.waitingOnBackgroundWork, {\n ...waitAttributes(now),\n item_count: work.items.length,\n error_count: work.errors.length,\n items: detail(work.items),\n errors: detail(work.errors),\n });\n },\n stoodDown() {\n record(AUTO_STOP_EVENT.stoodDown, { ...waitAttributes(options.now()), reason: 'pending_messages' });\n reset();\n },\n shutdownRequested() {\n record(AUTO_STOP_EVENT.shutdownRequested, waitAttributes(options.now()));\n reset();\n },\n reset,\n async dispose() {\n let timer: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n options.telemetry.shutdown().catch(() => undefined),\n new Promise((settle) => {\n timer = setTimeout(settle, flushTimeoutMs);\n timer.unref?.();\n }),\n ]);\n if (timer) clearTimeout(timer);\n },\n };\n}\n","import {\n BACKGROUND_WORK_HOLDING_PROVIDERS,\n DOOM_BACKGROUND_WORK_SERVICE,\n holdsSettledSession,\n readDoomBackgroundWorkService,\n type DoomBackgroundWorkService,\n} from '@agimon-ai/po-core/backgroundWork';\nimport type { Context } from '@deepseek-ai/cordis';\n\n/** What is holding a session open, named so a stuck shutdown can say why. */\nexport interface BackgroundWorkSummary {\n readonly active: boolean;\n /** `provider:id` for each item the session still owns. */\n readonly items: readonly string[];\n /** `provider: message` for each provider whose snapshot failed. */\n readonly errors: readonly string[];\n}\n\nexport interface BackgroundWorkGate {\n readonly plugin: (context: Context) => void;\n hasActiveWork(sessionId: string): boolean;\n inspect(sessionId: string): BackgroundWorkSummary;\n}\n\nconst NO_WORK: BackgroundWorkSummary = Object.freeze({ active: false, items: [], errors: [] });\n\n/**\n * Tracks the current background-work coordinator. Only runners and subagents\n * count, and it fails closed when their snapshot is uncertain.\n */\nexport function createBackgroundWorkGate(): BackgroundWorkGate {\n let backgroundWork: DoomBackgroundWorkService | undefined;\n const inspect = (sessionId: string): BackgroundWorkSummary => {\n if (!backgroundWork) return NO_WORK;\n try {\n const snapshot = backgroundWork.snapshot(sessionId);\n const items = snapshot.items.filter(holdsSettledSession).map((item) => `${item.provider}:${item.id}`);\n const errors = snapshot.errors\n .filter((error) => BACKGROUND_WORK_HOLDING_PROVIDERS.includes(error.provider))\n .map((error) => `${error.provider}: ${error.message}`);\n return { active: items.length > 0 || errors.length > 0, items, errors };\n } catch (error) {\n return {\n active: true,\n items: [],\n errors: [`snapshot: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n };\n return {\n plugin: (context) => {\n context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n backgroundWork = service;\n return () => {\n if (backgroundWork === service) backgroundWork = undefined;\n };\n });\n },\n hasActiveWork: (sessionId) => inspect(sessionId).active,\n inspect,\n };\n}\n","import type { DoomHeadlessHostService, DoomHeadlessHook } from '@agimon-ai/po-core/headless';\n\nimport { DEFAULT_AUTO_STOP_DELAYS } from '../../constants/idlePolicy';\nimport type { AutostopDiagnostics } from '../autostopDiagnostics/type';\nimport type { BackgroundWorkSummary } from '../backgroundWorkGate';\nimport { decideOnRecheck, decideOnSettled } from '../idlePolicy';\nimport type { AutoStopDelays } from '../idlePolicy/type';\nimport type { ServerIdleShutdown } from './type';\n\nconst NO_WORK: BackgroundWorkSummary = Object.freeze({ active: false, items: [], errors: [] });\n\nexport function createServerIdleShutdown(\n host: DoomHeadlessHostService,\n inspectBackgroundWork: () => BackgroundWorkSummary = () => NO_WORK,\n delays: AutoStopDelays = DEFAULT_AUTO_STOP_DELAYS,\n diagnostics?: Pick<AutostopDiagnostics, 'waiting' | 'stoodDown' | 'shutdownRequested' | 'reset'>,\n): ServerIdleShutdown {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let watching = false;\n let disposed = false;\n let generation = 0;\n const stopWatching = (): void => {\n generation += 1;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n watching = false;\n diagnostics?.reset();\n };\n const schedule = (delayMs: number): void => {\n if (disposed) return;\n if (timer !== undefined) clearTimeout(timer);\n watching = true;\n const expected = ++generation;\n timer = setTimeout(() => {\n timer = undefined;\n void recheck(expected).catch(() => {\n // Unknown activity cannot justify stopping the session. Stand down.\n if (expected === generation) stopWatching();\n });\n }, delayMs);\n };\n const recheck = async (expected: number): Promise<void> => {\n if (disposed || !watching || expected !== generation) return;\n const work = inspectBackgroundWork();\n if (work.active) {\n diagnostics?.waiting(work);\n schedule(delays.cooldownMs);\n return;\n }\n const activity = await host.context.session.activity();\n if (disposed || !watching || expected !== generation) return;\n // Background work can start while authoritative activity is being read.\n const currentWork = inspectBackgroundWork();\n if (currentWork.active) {\n diagnostics?.waiting(currentWork);\n schedule(delays.cooldownMs);\n return;\n }\n const decision = decideOnRecheck(activity, delays);\n if (decision.action === 'shutdown') {\n diagnostics?.shutdownRequested();\n stopWatching();\n host.context.shutdown();\n } else if (decision.action === 'stand-down') {\n diagnostics?.stoodDown();\n stopWatching();\n } else schedule(decision.delayMs);\n };\n\n const hooks: DoomHeadlessHook[] = [\n { event: 'agent_start', handle: stopWatching },\n {\n event: 'agent_settled',\n async handle() {\n stopWatching();\n const expected = generation;\n const activity = await host.context.session.activity();\n if (disposed || expected !== generation) return;\n const decision = decideOnSettled(activity, delays);\n if (decision.action === 'recheck') schedule(decision.delayMs);\n else diagnostics?.stoodDown();\n },\n },\n { event: 'session_shutdown', handle: stopWatching },\n { event: 'session_tree', handle: () => recheck(generation) },\n ];\n return {\n hooks,\n dispose() {\n disposed = true;\n stopWatching();\n },\n };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\nimport { createDoomTelemetry } from '@agimon-ai/po-telemetry';\n\nimport { createAutostopDiagnostics } from '../../../../services/autostopDiagnostics';\nimport { createBackgroundWorkGate } from '../../../../services/backgroundWorkGate';\nimport { createServerIdleShutdown } from '../../../../services/serverIdleShutdown';\nexport default defineRoot(({ agent }: DoomServerPluginContext) => {\n if (!agent) throw new Error('Autostop requires a session host.');\n const backgroundWork = createBackgroundWorkGate();\n const diagnostics = createAutostopDiagnostics({\n telemetry: createDoomTelemetry({\n serviceName: 'doom-autostop',\n packageName: '@agimon-ai/po-autostop',\n enableLogs: true,\n enableTraces: false,\n }),\n now: Date.now,\n });\n const watch = createServerIdleShutdown(\n agent,\n () => backgroundWork.inspect(agent.context.sessionId),\n undefined,\n diagnostics,\n );\n return {\n value: watch,\n services: [backgroundWork.plugin],\n onDispose: async () => {\n watch.dispose();\n await diagnostics.dispose();\n },\n };\n});\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'agent_settled')!,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'agent_start')!,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'session_shutdown')!,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'session_tree')!,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport hookAgentSettled from '../src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server';\nimport hookAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/agent-start.server';\nimport hookSessionShutdown from '../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server';\nimport hookSessionTree from '../src/extensions/workspaces/sessions/(backend)/hook/session-tree.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-autostop',\n providerOverrides: composeServerProviderOverrides(rootSession),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get hooks(): DoomServerSessionPlugin['hooks'] { return [via({ event: 'agent_settled' }, at(hookAgentSettled, contextSession)), via({ event: 'agent_start' }, at(hookAgentStart, contextSession)), via({ event: 'session_shutdown' }, at(hookSessionShutdown, contextSession)), via({ event: 'session_tree' }, at(hookSessionTree, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":";;;;;;;;;;AAQA,SAAS,OAAO,QAAmC;CACjD,MAAM,SAAS,OAAO,KAAK,IAAI;CAC/B,OAAO,OAAO,SAAA,MAAuC,GAAG,OAAO,MAAM,GAAA,GAAkC,EAAE,KAAK;AAChH;;;;;;;;AASA,SAAgB,0BAA0B,SAA0D;CAClG,MAAM,mBAAmB,QAAQ,oBAAA;CACjC,MAAM,iBAAiB,QAAQ,kBAAA;CAC/B,IAAI;CACJ,IAAI;CACJ,IAAI,YAAY;CAEhB,MAAM,UAAU,OAAe,eAA8C;EAE3E,QAAQ,UAAU,YAAY,OAAO,UAAU,CAAC,CAAC,YAAY,KAAA,CAAS;CACxE;CACA,MAAM,kBAAkB,SAA0C;EAChE,gBAAgB;EAChB,WAAW,kBAAkB,KAAA,IAAY,IAAI,MAAM;CACrD;CACA,MAAM,cAAoB;EACxB,gBAAgB,KAAA;EAChB,iBAAiB,KAAA;EACjB,YAAY;CACd;CAEA,OAAO;EACL,QAAQ,MAAM;GACZ,MAAM,MAAM,QAAQ,IAAI;GACxB,kBAAkB;GAClB,aAAa;GACb,IAAI,mBAAmB,KAAA,KAAa,MAAM,iBAAiB,kBAAkB;GAC7E,iBAAiB;GACjB,OAAOA,mBAAAA,gBAAgB,yBAAyB;IAC9C,GAAG,eAAe,GAAG;IACrB,YAAY,KAAK,MAAM;IACvB,aAAa,KAAK,OAAO;IACzB,OAAO,OAAO,KAAK,KAAK;IACxB,QAAQ,OAAO,KAAK,MAAM;GAC5B,CAAC;EACH;EACA,YAAY;GACV,OAAOA,mBAAAA,gBAAgB,WAAW;IAAE,GAAG,eAAe,QAAQ,IAAI,CAAC;IAAG,QAAQ;GAAmB,CAAC;GAClG,MAAM;EACR;EACA,oBAAoB;GAClB,OAAOA,mBAAAA,gBAAgB,mBAAmB,eAAe,QAAQ,IAAI,CAAC,CAAC;GACvE,MAAM;EACR;EACA;EACA,MAAM,UAAU;GACd,IAAI;GACJ,MAAM,QAAQ,KAAK,CACjB,QAAQ,UAAU,SAAS,CAAC,CAAC,YAAY,KAAA,CAAS,GAClD,IAAI,SAAS,WAAW;IACtB,QAAQ,WAAW,QAAQ,cAAc;IACzC,MAAM,QAAQ;GAChB,CAAC,CACH,CAAC;GACD,IAAI,OAAO,aAAa,KAAK;EAC/B;CACF;AACF;;;ACrDA,MAAMC,YAAiC,OAAO,OAAO;CAAE,QAAQ;CAAO,OAAO,CAAC;CAAG,QAAQ,CAAC;AAAE,CAAC;;;;;AAM7F,SAAgB,2BAA+C;CAC7D,IAAI;CACJ,MAAM,WAAW,cAA6C;EAC5D,IAAI,CAAC,gBAAgB,OAAOA;EAC5B,IAAI;GACF,MAAM,WAAW,eAAe,SAAS,SAAS;GAClD,MAAM,QAAQ,SAAS,MAAM,OAAOC,kCAAAA,mBAAmB,CAAC,CAAC,KAAK,SAAS,GAAG,KAAK,SAAS,GAAG,KAAK,IAAI;GACpG,MAAM,SAAS,SAAS,OACrB,QAAQ,UAAUC,kCAAAA,kCAAkC,SAAS,MAAM,QAAQ,CAAC,CAAC,CAC7E,KAAK,UAAU,GAAG,MAAM,SAAS,IAAI,MAAM,SAAS;GACvD,OAAO;IAAE,QAAQ,MAAM,SAAS,KAAK,OAAO,SAAS;IAAG;IAAO;GAAO;EACxE,SAAS,OAAO;GACd,OAAO;IACL,QAAQ;IACR,OAAO,CAAC;IACR,QAAQ,CAAC,aAAa,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG;GAChF;EACF;CACF;CACA,OAAO;EACL,SAAS,YAAY;GACnB,QAAQ,OAAO,CAACC,kCAAAA,4BAA4B,IAAI,mBAAmB;IACjE,MAAM,WAAA,GAAUC,kCAAAA,8BAAAA,CAA8B,cAAc;IAC5D,IAAI,CAAC,SAAS,OAAO,KAAA;IACrB,iBAAiB;IACjB,aAAa;KACX,IAAI,mBAAmB,SAAS,iBAAiB,KAAA;IACnD;GACF,CAAC;EACH;EACA,gBAAgB,cAAc,QAAQ,SAAS,CAAC,CAAC;EACjD;CACF;AACF;;;ACtDA,MAAM,UAAiC,OAAO,OAAO;CAAE,QAAQ;CAAO,OAAO,CAAC;CAAG,QAAQ,CAAC;AAAE,CAAC;AAE7F,SAAgB,yBACd,MACA,8BAA2D,SAC3D,SAAyBC,mBAAAA,0BACzB,aACoB;CACpB,IAAI;CACJ,IAAI,WAAW;CACf,IAAI,WAAW;CACf,IAAI,aAAa;CACjB,MAAM,qBAA2B;EAC/B,cAAc;EACd,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;EAC3C,QAAQ,KAAA;EACR,WAAW;EACX,aAAa,MAAM;CACrB;CACA,MAAM,YAAY,YAA0B;EAC1C,IAAI,UAAU;EACd,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;EAC3C,WAAW;EACX,MAAM,WAAW,EAAE;EACnB,QAAQ,iBAAiB;GACvB,QAAQ,KAAA;GACR,QAAa,QAAQ,CAAC,CAAC,YAAY;IAEjC,IAAI,aAAa,YAAY,aAAa;GAC5C,CAAC;EACH,GAAG,OAAO;CACZ;CACA,MAAM,UAAU,OAAO,aAAoC;EACzD,IAAI,YAAY,CAAC,YAAY,aAAa,YAAY;EACtD,MAAM,OAAO,sBAAsB;EACnC,IAAI,KAAK,QAAQ;GACf,aAAa,QAAQ,IAAI;GACzB,SAAS,OAAO,UAAU;GAC1B;EACF;EACA,MAAM,WAAW,MAAM,KAAK,QAAQ,QAAQ,SAAS;EACrD,IAAI,YAAY,CAAC,YAAY,aAAa,YAAY;EAEtD,MAAM,cAAc,sBAAsB;EAC1C,IAAI,YAAY,QAAQ;GACtB,aAAa,QAAQ,WAAW;GAChC,SAAS,OAAO,UAAU;GAC1B;EACF;EACA,MAAM,WAAWC,mBAAAA,gBAAgB,UAAU,MAAM;EACjD,IAAI,SAAS,WAAW,YAAY;GAClC,aAAa,kBAAkB;GAC/B,aAAa;GACb,KAAK,QAAQ,SAAS;EACxB,OAAO,IAAI,SAAS,WAAW,cAAc;GAC3C,aAAa,UAAU;GACvB,aAAa;EACf,OAAO,SAAS,SAAS,OAAO;CAClC;CAmBA,OAAO;EACL,OAAA;GAjBA;IAAE,OAAO;IAAe,QAAQ;GAAa;GAC7C;IACE,OAAO;IACP,MAAM,SAAS;KACb,aAAa;KACb,MAAM,WAAW;KACjB,MAAM,WAAW,MAAM,KAAK,QAAQ,QAAQ,SAAS;KACrD,IAAI,YAAY,aAAa,YAAY;KACzC,MAAM,WAAWC,mBAAAA,gBAAgB,UAAU,MAAM;KACjD,IAAI,SAAS,WAAW,WAAW,SAAS,SAAS,OAAO;UACvD,aAAa,UAAU;IAC9B;GACF;GACA;IAAE,OAAO;IAAoB,QAAQ;GAAa;GAClD;IAAE,OAAO;IAAgB,cAAc,QAAQ,UAAU;GAAE;EAGvD;EACJ,UAAU;GACR,WAAW;GACX,aAAa;EACf;CACF;AACF;;;ACtFA,IAAA,uBAAA,GAAeC,iCAAAA,WAAAA,EAAY,EAAE,YAAqC;CAChE,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,mCAAmC;CAC/D,MAAM,iBAAiB,yBAAyB;CAChD,MAAM,cAAc,0BAA0B;EAC5C,YAAA,GAAWC,wBAAAA,oBAAAA,CAAoB;GAC7B,aAAa;GACb,aAAa;GACb,YAAY;GACZ,cAAc;EAChB,CAAC;EACD,KAAK,KAAK;CACZ,CAAC;CACD,MAAM,QAAQ,yBACZ,aACM,eAAe,QAAQ,MAAM,QAAQ,SAAS,GACpD,KAAA,GACA,WACF;CACA,OAAO;EACL,OAAO;EACP,UAAU,CAAC,eAAe,MAAM;EAChC,WAAW,YAAY;GACrB,MAAM,QAAQ;GACd,MAAM,YAAY,QAAQ;EAC5B;CACF;AACF,CAAC;;;AC7BD,IAAA,gCAAA,GAAeC,iCAAAA,WAAAA,EAAY,YACzB,QAAQ,KAAK,MAAM,MAAM,SAAS,KAAK,UAAU,eAAe,CAClE;;;ACFA,IAAA,8BAAA,GAAeC,iCAAAA,WAAAA,EAAY,YACzB,QAAQ,KAAK,MAAM,MAAM,SAAS,KAAK,UAAU,aAAa,CAChE;;;ACFA,IAAA,mCAAA,GAAeC,iCAAAA,WAAAA,EAAY,YACzB,QAAQ,KAAK,MAAM,MAAM,SAAS,KAAK,UAAU,kBAAkB,CACrE;;;ACFA,IAAA,+BAAA,GAAeC,iCAAAA,WAAAA,EAAY,YACzB,QAAQ,KAAK,MAAM,MAAM,SAAS,KAAK,UAAU,cAAc,CACjE;;;ACMA,MAAM,MAAY,OAA0B,YAC1C,OAAO,UAAU,aAAc,MAAwB,OAAO,IAAI;AACpE,MAAM,OAAU,UAAmC,UAChD,UAAU,KAAA,IAAY,KAAA,IAAY;CAAE,GAAG;CAAU,GAAI;AAAiB;AAEzE,MAAa,SAAA,GAAQC,+BAAAA,mBAAAA,CAAmB;CACtC,MAAM;CACN,oBAAA,GAAmBC,+BAAAA,+BAAAA,CAA+BC,mBAAW;CAC7D,SAAS,OAAO,YAAY;EAC1B,MAAM,eAAe,MAAMA,oBAAY,OAAO;EAC9C,MAAM,iBAAiB;GAAE,GAAG;GAAS,MAAM,aAAa;EAAM;EAC9D,OAAO;GACL,IAAA,GAAGC,iCAAAA,iBAAAA,CAAiB,YAAY;GAChC,IAAI,QAA0C;IAAE,OAAO;KAAC,IAAI,EAAE,OAAO,gBAAgB,GAAG,GAAGC,8BAAkB,cAAc,CAAC;KAAG,IAAI,EAAE,OAAO,cAAc,GAAG,GAAGC,4BAAgB,cAAc,CAAC;KAAG,IAAI,EAAE,OAAO,mBAAmB,GAAG,GAAGC,iCAAqB,cAAc,CAAC;KAAG,IAAI,EAAE,OAAO,eAAe,GAAG,GAAGC,6BAAiB,cAAc,CAAC;IAAC;GAAG;GACrV,UAAU,CAAC,GAAI,aAAa,YAAY,CAAC,CAAE;GAC3C,IAAI,aAAoD;IAAE,OAAO,CAAC,GAAI,aAAa,cAAc,CAAC,CAAE;GAAG;EACzG;CACF;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../../generated/server.ts"],"mappings":";qBAiBa,gDAAK"}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { i as AUTO_STOP_EVENT, n as decideOnSettled, s as DEFAULT_AUTO_STOP_DELAYS, t as decideOnRecheck } from "../idlePolicy-BNLdwumq.mjs";
|
|
2
|
+
import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
|
|
3
|
+
import { composeRootHooks, defineHook, defineRoot } from "@agimon-ai/po-core/extensionFile";
|
|
4
|
+
import { createDoomTelemetry } from "@agimon-ai/po-telemetry";
|
|
5
|
+
import { BACKGROUND_WORK_HOLDING_PROVIDERS, DOOM_BACKGROUND_WORK_SERVICE, holdsSettledSession, readDoomBackgroundWorkService } from "@agimon-ai/po-core/backgroundWork";
|
|
6
|
+
//#region src/services/autostopDiagnostics/index.ts
|
|
7
|
+
function detail(values) {
|
|
8
|
+
const joined = values.join(", ");
|
|
9
|
+
return joined.length > 500 ? `${joined.slice(0, 499)}…` : joined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Report what an idle session is waiting on before it stops.
|
|
13
|
+
*
|
|
14
|
+
* The first wait is reported at once and a long one again each interval, so a
|
|
15
|
+
* session held open for an hour names the work holding it without flooding
|
|
16
|
+
* the sink every cooldown.
|
|
17
|
+
*/
|
|
18
|
+
function createAutostopDiagnostics(options) {
|
|
19
|
+
const reportIntervalMs = options.reportIntervalMs ?? 6e4;
|
|
20
|
+
const flushTimeoutMs = options.flushTimeoutMs ?? 2e3;
|
|
21
|
+
let waitStartedAt;
|
|
22
|
+
let lastReportedAt;
|
|
23
|
+
let deferrals = 0;
|
|
24
|
+
const record = (event, attributes) => {
|
|
25
|
+
options.telemetry.recordEvent(event, attributes).catch(() => void 0);
|
|
26
|
+
};
|
|
27
|
+
const waitAttributes = (now) => ({
|
|
28
|
+
deferral_count: deferrals,
|
|
29
|
+
waited_ms: waitStartedAt === void 0 ? 0 : now - waitStartedAt
|
|
30
|
+
});
|
|
31
|
+
const reset = () => {
|
|
32
|
+
waitStartedAt = void 0;
|
|
33
|
+
lastReportedAt = void 0;
|
|
34
|
+
deferrals = 0;
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
waiting(work) {
|
|
38
|
+
const now = options.now();
|
|
39
|
+
waitStartedAt ??= now;
|
|
40
|
+
deferrals += 1;
|
|
41
|
+
if (lastReportedAt !== void 0 && now - lastReportedAt < reportIntervalMs) return;
|
|
42
|
+
lastReportedAt = now;
|
|
43
|
+
record(AUTO_STOP_EVENT.waitingOnBackgroundWork, {
|
|
44
|
+
...waitAttributes(now),
|
|
45
|
+
item_count: work.items.length,
|
|
46
|
+
error_count: work.errors.length,
|
|
47
|
+
items: detail(work.items),
|
|
48
|
+
errors: detail(work.errors)
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
stoodDown() {
|
|
52
|
+
record(AUTO_STOP_EVENT.stoodDown, {
|
|
53
|
+
...waitAttributes(options.now()),
|
|
54
|
+
reason: "pending_messages"
|
|
55
|
+
});
|
|
56
|
+
reset();
|
|
57
|
+
},
|
|
58
|
+
shutdownRequested() {
|
|
59
|
+
record(AUTO_STOP_EVENT.shutdownRequested, waitAttributes(options.now()));
|
|
60
|
+
reset();
|
|
61
|
+
},
|
|
62
|
+
reset,
|
|
63
|
+
async dispose() {
|
|
64
|
+
let timer;
|
|
65
|
+
await Promise.race([options.telemetry.shutdown().catch(() => void 0), new Promise((settle) => {
|
|
66
|
+
timer = setTimeout(settle, flushTimeoutMs);
|
|
67
|
+
timer.unref?.();
|
|
68
|
+
})]);
|
|
69
|
+
if (timer) clearTimeout(timer);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region src/services/backgroundWorkGate/index.ts
|
|
75
|
+
const NO_WORK$1 = Object.freeze({
|
|
76
|
+
active: false,
|
|
77
|
+
items: [],
|
|
78
|
+
errors: []
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* Tracks the current background-work coordinator. Only runners and subagents
|
|
82
|
+
* count, and it fails closed when their snapshot is uncertain.
|
|
83
|
+
*/
|
|
84
|
+
function createBackgroundWorkGate() {
|
|
85
|
+
let backgroundWork;
|
|
86
|
+
const inspect = (sessionId) => {
|
|
87
|
+
if (!backgroundWork) return NO_WORK$1;
|
|
88
|
+
try {
|
|
89
|
+
const snapshot = backgroundWork.snapshot(sessionId);
|
|
90
|
+
const items = snapshot.items.filter(holdsSettledSession).map((item) => `${item.provider}:${item.id}`);
|
|
91
|
+
const errors = snapshot.errors.filter((error) => BACKGROUND_WORK_HOLDING_PROVIDERS.includes(error.provider)).map((error) => `${error.provider}: ${error.message}`);
|
|
92
|
+
return {
|
|
93
|
+
active: items.length > 0 || errors.length > 0,
|
|
94
|
+
items,
|
|
95
|
+
errors
|
|
96
|
+
};
|
|
97
|
+
} catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
active: true,
|
|
100
|
+
items: [],
|
|
101
|
+
errors: [`snapshot: ${error instanceof Error ? error.message : String(error)}`]
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
return {
|
|
106
|
+
plugin: (context) => {
|
|
107
|
+
context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {
|
|
108
|
+
const service = readDoomBackgroundWorkService(serviceContext);
|
|
109
|
+
if (!service) return void 0;
|
|
110
|
+
backgroundWork = service;
|
|
111
|
+
return () => {
|
|
112
|
+
if (backgroundWork === service) backgroundWork = void 0;
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
hasActiveWork: (sessionId) => inspect(sessionId).active,
|
|
117
|
+
inspect
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/services/serverIdleShutdown/index.ts
|
|
122
|
+
const NO_WORK = Object.freeze({
|
|
123
|
+
active: false,
|
|
124
|
+
items: [],
|
|
125
|
+
errors: []
|
|
126
|
+
});
|
|
127
|
+
function createServerIdleShutdown(host, inspectBackgroundWork = () => NO_WORK, delays = DEFAULT_AUTO_STOP_DELAYS, diagnostics) {
|
|
128
|
+
let timer;
|
|
129
|
+
let watching = false;
|
|
130
|
+
let disposed = false;
|
|
131
|
+
let generation = 0;
|
|
132
|
+
const stopWatching = () => {
|
|
133
|
+
generation += 1;
|
|
134
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
135
|
+
timer = void 0;
|
|
136
|
+
watching = false;
|
|
137
|
+
diagnostics?.reset();
|
|
138
|
+
};
|
|
139
|
+
const schedule = (delayMs) => {
|
|
140
|
+
if (disposed) return;
|
|
141
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
142
|
+
watching = true;
|
|
143
|
+
const expected = ++generation;
|
|
144
|
+
timer = setTimeout(() => {
|
|
145
|
+
timer = void 0;
|
|
146
|
+
recheck(expected).catch(() => {
|
|
147
|
+
if (expected === generation) stopWatching();
|
|
148
|
+
});
|
|
149
|
+
}, delayMs);
|
|
150
|
+
};
|
|
151
|
+
const recheck = async (expected) => {
|
|
152
|
+
if (disposed || !watching || expected !== generation) return;
|
|
153
|
+
const work = inspectBackgroundWork();
|
|
154
|
+
if (work.active) {
|
|
155
|
+
diagnostics?.waiting(work);
|
|
156
|
+
schedule(delays.cooldownMs);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const activity = await host.context.session.activity();
|
|
160
|
+
if (disposed || !watching || expected !== generation) return;
|
|
161
|
+
const currentWork = inspectBackgroundWork();
|
|
162
|
+
if (currentWork.active) {
|
|
163
|
+
diagnostics?.waiting(currentWork);
|
|
164
|
+
schedule(delays.cooldownMs);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const decision = decideOnRecheck(activity, delays);
|
|
168
|
+
if (decision.action === "shutdown") {
|
|
169
|
+
diagnostics?.shutdownRequested();
|
|
170
|
+
stopWatching();
|
|
171
|
+
host.context.shutdown();
|
|
172
|
+
} else if (decision.action === "stand-down") {
|
|
173
|
+
diagnostics?.stoodDown();
|
|
174
|
+
stopWatching();
|
|
175
|
+
} else schedule(decision.delayMs);
|
|
176
|
+
};
|
|
177
|
+
return {
|
|
178
|
+
hooks: [
|
|
179
|
+
{
|
|
180
|
+
event: "agent_start",
|
|
181
|
+
handle: stopWatching
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
event: "agent_settled",
|
|
185
|
+
async handle() {
|
|
186
|
+
stopWatching();
|
|
187
|
+
const expected = generation;
|
|
188
|
+
const activity = await host.context.session.activity();
|
|
189
|
+
if (disposed || expected !== generation) return;
|
|
190
|
+
const decision = decideOnSettled(activity, delays);
|
|
191
|
+
if (decision.action === "recheck") schedule(decision.delayMs);
|
|
192
|
+
else diagnostics?.stoodDown();
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
event: "session_shutdown",
|
|
197
|
+
handle: stopWatching
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
event: "session_tree",
|
|
201
|
+
handle: () => recheck(generation)
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
dispose() {
|
|
205
|
+
disposed = true;
|
|
206
|
+
stopWatching();
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/extensions/workspaces/sessions/(backend)/root.server.ts
|
|
212
|
+
var root_server_default = defineRoot(({ agent }) => {
|
|
213
|
+
if (!agent) throw new Error("Autostop requires a session host.");
|
|
214
|
+
const backgroundWork = createBackgroundWorkGate();
|
|
215
|
+
const diagnostics = createAutostopDiagnostics({
|
|
216
|
+
telemetry: createDoomTelemetry({
|
|
217
|
+
serviceName: "doom-autostop",
|
|
218
|
+
packageName: "@agimon-ai/po-autostop",
|
|
219
|
+
enableLogs: true,
|
|
220
|
+
enableTraces: false
|
|
221
|
+
}),
|
|
222
|
+
now: Date.now
|
|
223
|
+
});
|
|
224
|
+
const watch = createServerIdleShutdown(agent, () => backgroundWork.inspect(agent.context.sessionId), void 0, diagnostics);
|
|
225
|
+
return {
|
|
226
|
+
value: watch,
|
|
227
|
+
services: [backgroundWork.plugin],
|
|
228
|
+
onDispose: async () => {
|
|
229
|
+
watch.dispose();
|
|
230
|
+
await diagnostics.dispose();
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts
|
|
236
|
+
var agent_settled_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "agent_settled"));
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/agent-start.server.ts
|
|
239
|
+
var agent_start_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "agent_start"));
|
|
240
|
+
//#endregion
|
|
241
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
|
|
242
|
+
var session_shutdown_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "session_shutdown"));
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/extensions/workspaces/sessions/(backend)/hook/session-tree.server.ts
|
|
245
|
+
var session_tree_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "session_tree"));
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region generated/server.ts
|
|
248
|
+
const at = (value, context) => typeof value === "function" ? value(context) : value;
|
|
249
|
+
const via = (identity, value) => value === void 0 ? void 0 : {
|
|
250
|
+
...identity,
|
|
251
|
+
...value
|
|
252
|
+
};
|
|
253
|
+
const facet = defineServerPlugin({
|
|
254
|
+
name: "@agimon-ai/po-autostop",
|
|
255
|
+
providerOverrides: composeServerProviderOverrides(root_server_default),
|
|
256
|
+
session: async (context) => {
|
|
257
|
+
const scopeSession = await root_server_default(context);
|
|
258
|
+
const contextSession = {
|
|
259
|
+
...context,
|
|
260
|
+
root: scopeSession.value
|
|
261
|
+
};
|
|
262
|
+
return {
|
|
263
|
+
...composeRootHooks(scopeSession),
|
|
264
|
+
get hooks() {
|
|
265
|
+
return [
|
|
266
|
+
via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)),
|
|
267
|
+
via({ event: "agent_start" }, at(agent_start_server_default, contextSession)),
|
|
268
|
+
via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession)),
|
|
269
|
+
via({ event: "session_tree" }, at(session_tree_server_default, contextSession))
|
|
270
|
+
];
|
|
271
|
+
},
|
|
272
|
+
services: [...scopeSession.services ?? []],
|
|
273
|
+
get activities() {
|
|
274
|
+
return [...scopeSession.activities ?? []];
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
//#endregion
|
|
280
|
+
export { facet as default, facet };
|
|
281
|
+
|
|
282
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":["NO_WORK","rootSession","hookAgentSettled","hookAgentStart","hookSessionShutdown","hookSessionTree"],"sources":["../../src/services/autostopDiagnostics/index.ts","../../src/services/backgroundWorkGate/index.ts","../../src/services/serverIdleShutdown/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/agent-start.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts","../../src/extensions/workspaces/sessions/(backend)/hook/session-tree.server.ts","../../generated/server.ts"],"sourcesContent":["import {\n AUTO_STOP_DETAIL_MAX_LENGTH,\n AUTO_STOP_EVENT,\n AUTO_STOP_REPORT_INTERVAL_MS,\n AUTO_STOP_TELEMETRY_FLUSH_MS,\n} from '../../constants/idlePolicy';\nimport type { AutostopDiagnostics, AutostopDiagnosticsOptions, AutostopEventAttributes } from './type';\n\nfunction detail(values: readonly string[]): string {\n const joined = values.join(', ');\n return joined.length > AUTO_STOP_DETAIL_MAX_LENGTH ? `${joined.slice(0, AUTO_STOP_DETAIL_MAX_LENGTH - 1)}…` : joined;\n}\n\n/**\n * Report what an idle session is waiting on before it stops.\n *\n * The first wait is reported at once and a long one again each interval, so a\n * session held open for an hour names the work holding it without flooding\n * the sink every cooldown.\n */\nexport function createAutostopDiagnostics(options: AutostopDiagnosticsOptions): AutostopDiagnostics {\n const reportIntervalMs = options.reportIntervalMs ?? AUTO_STOP_REPORT_INTERVAL_MS;\n const flushTimeoutMs = options.flushTimeoutMs ?? AUTO_STOP_TELEMETRY_FLUSH_MS;\n let waitStartedAt: number | undefined;\n let lastReportedAt: number | undefined;\n let deferrals = 0;\n\n const record = (event: string, attributes: AutostopEventAttributes): void => {\n // Diagnostics must never change whether the session stops.\n options.telemetry.recordEvent(event, attributes).catch(() => undefined);\n };\n const waitAttributes = (now: number): AutostopEventAttributes => ({\n deferral_count: deferrals,\n waited_ms: waitStartedAt === undefined ? 0 : now - waitStartedAt,\n });\n const reset = (): void => {\n waitStartedAt = undefined;\n lastReportedAt = undefined;\n deferrals = 0;\n };\n\n return {\n waiting(work) {\n const now = options.now();\n waitStartedAt ??= now;\n deferrals += 1;\n if (lastReportedAt !== undefined && now - lastReportedAt < reportIntervalMs) return;\n lastReportedAt = now;\n record(AUTO_STOP_EVENT.waitingOnBackgroundWork, {\n ...waitAttributes(now),\n item_count: work.items.length,\n error_count: work.errors.length,\n items: detail(work.items),\n errors: detail(work.errors),\n });\n },\n stoodDown() {\n record(AUTO_STOP_EVENT.stoodDown, { ...waitAttributes(options.now()), reason: 'pending_messages' });\n reset();\n },\n shutdownRequested() {\n record(AUTO_STOP_EVENT.shutdownRequested, waitAttributes(options.now()));\n reset();\n },\n reset,\n async dispose() {\n let timer: ReturnType<typeof setTimeout> | undefined;\n await Promise.race([\n options.telemetry.shutdown().catch(() => undefined),\n new Promise((settle) => {\n timer = setTimeout(settle, flushTimeoutMs);\n timer.unref?.();\n }),\n ]);\n if (timer) clearTimeout(timer);\n },\n };\n}\n","import {\n BACKGROUND_WORK_HOLDING_PROVIDERS,\n DOOM_BACKGROUND_WORK_SERVICE,\n holdsSettledSession,\n readDoomBackgroundWorkService,\n type DoomBackgroundWorkService,\n} from '@agimon-ai/po-core/backgroundWork';\nimport type { Context } from '@deepseek-ai/cordis';\n\n/** What is holding a session open, named so a stuck shutdown can say why. */\nexport interface BackgroundWorkSummary {\n readonly active: boolean;\n /** `provider:id` for each item the session still owns. */\n readonly items: readonly string[];\n /** `provider: message` for each provider whose snapshot failed. */\n readonly errors: readonly string[];\n}\n\nexport interface BackgroundWorkGate {\n readonly plugin: (context: Context) => void;\n hasActiveWork(sessionId: string): boolean;\n inspect(sessionId: string): BackgroundWorkSummary;\n}\n\nconst NO_WORK: BackgroundWorkSummary = Object.freeze({ active: false, items: [], errors: [] });\n\n/**\n * Tracks the current background-work coordinator. Only runners and subagents\n * count, and it fails closed when their snapshot is uncertain.\n */\nexport function createBackgroundWorkGate(): BackgroundWorkGate {\n let backgroundWork: DoomBackgroundWorkService | undefined;\n const inspect = (sessionId: string): BackgroundWorkSummary => {\n if (!backgroundWork) return NO_WORK;\n try {\n const snapshot = backgroundWork.snapshot(sessionId);\n const items = snapshot.items.filter(holdsSettledSession).map((item) => `${item.provider}:${item.id}`);\n const errors = snapshot.errors\n .filter((error) => BACKGROUND_WORK_HOLDING_PROVIDERS.includes(error.provider))\n .map((error) => `${error.provider}: ${error.message}`);\n return { active: items.length > 0 || errors.length > 0, items, errors };\n } catch (error) {\n return {\n active: true,\n items: [],\n errors: [`snapshot: ${error instanceof Error ? error.message : String(error)}`],\n };\n }\n };\n return {\n plugin: (context) => {\n context.inject([DOOM_BACKGROUND_WORK_SERVICE], (serviceContext) => {\n const service = readDoomBackgroundWorkService(serviceContext);\n if (!service) return undefined;\n backgroundWork = service;\n return () => {\n if (backgroundWork === service) backgroundWork = undefined;\n };\n });\n },\n hasActiveWork: (sessionId) => inspect(sessionId).active,\n inspect,\n };\n}\n","import type { DoomHeadlessHostService, DoomHeadlessHook } from '@agimon-ai/po-core/headless';\n\nimport { DEFAULT_AUTO_STOP_DELAYS } from '../../constants/idlePolicy';\nimport type { AutostopDiagnostics } from '../autostopDiagnostics/type';\nimport type { BackgroundWorkSummary } from '../backgroundWorkGate';\nimport { decideOnRecheck, decideOnSettled } from '../idlePolicy';\nimport type { AutoStopDelays } from '../idlePolicy/type';\nimport type { ServerIdleShutdown } from './type';\n\nconst NO_WORK: BackgroundWorkSummary = Object.freeze({ active: false, items: [], errors: [] });\n\nexport function createServerIdleShutdown(\n host: DoomHeadlessHostService,\n inspectBackgroundWork: () => BackgroundWorkSummary = () => NO_WORK,\n delays: AutoStopDelays = DEFAULT_AUTO_STOP_DELAYS,\n diagnostics?: Pick<AutostopDiagnostics, 'waiting' | 'stoodDown' | 'shutdownRequested' | 'reset'>,\n): ServerIdleShutdown {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let watching = false;\n let disposed = false;\n let generation = 0;\n const stopWatching = (): void => {\n generation += 1;\n if (timer !== undefined) clearTimeout(timer);\n timer = undefined;\n watching = false;\n diagnostics?.reset();\n };\n const schedule = (delayMs: number): void => {\n if (disposed) return;\n if (timer !== undefined) clearTimeout(timer);\n watching = true;\n const expected = ++generation;\n timer = setTimeout(() => {\n timer = undefined;\n void recheck(expected).catch(() => {\n // Unknown activity cannot justify stopping the session. Stand down.\n if (expected === generation) stopWatching();\n });\n }, delayMs);\n };\n const recheck = async (expected: number): Promise<void> => {\n if (disposed || !watching || expected !== generation) return;\n const work = inspectBackgroundWork();\n if (work.active) {\n diagnostics?.waiting(work);\n schedule(delays.cooldownMs);\n return;\n }\n const activity = await host.context.session.activity();\n if (disposed || !watching || expected !== generation) return;\n // Background work can start while authoritative activity is being read.\n const currentWork = inspectBackgroundWork();\n if (currentWork.active) {\n diagnostics?.waiting(currentWork);\n schedule(delays.cooldownMs);\n return;\n }\n const decision = decideOnRecheck(activity, delays);\n if (decision.action === 'shutdown') {\n diagnostics?.shutdownRequested();\n stopWatching();\n host.context.shutdown();\n } else if (decision.action === 'stand-down') {\n diagnostics?.stoodDown();\n stopWatching();\n } else schedule(decision.delayMs);\n };\n\n const hooks: DoomHeadlessHook[] = [\n { event: 'agent_start', handle: stopWatching },\n {\n event: 'agent_settled',\n async handle() {\n stopWatching();\n const expected = generation;\n const activity = await host.context.session.activity();\n if (disposed || expected !== generation) return;\n const decision = decideOnSettled(activity, delays);\n if (decision.action === 'recheck') schedule(decision.delayMs);\n else diagnostics?.stoodDown();\n },\n },\n { event: 'session_shutdown', handle: stopWatching },\n { event: 'session_tree', handle: () => recheck(generation) },\n ];\n return {\n hooks,\n dispose() {\n disposed = true;\n stopWatching();\n },\n };\n}\n","import { defineRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\nimport { createDoomTelemetry } from '@agimon-ai/po-telemetry';\n\nimport { createAutostopDiagnostics } from '../../../../services/autostopDiagnostics';\nimport { createBackgroundWorkGate } from '../../../../services/backgroundWorkGate';\nimport { createServerIdleShutdown } from '../../../../services/serverIdleShutdown';\nexport default defineRoot(({ agent }: DoomServerPluginContext) => {\n if (!agent) throw new Error('Autostop requires a session host.');\n const backgroundWork = createBackgroundWorkGate();\n const diagnostics = createAutostopDiagnostics({\n telemetry: createDoomTelemetry({\n serviceName: 'doom-autostop',\n packageName: '@agimon-ai/po-autostop',\n enableLogs: true,\n enableTraces: false,\n }),\n now: Date.now,\n });\n const watch = createServerIdleShutdown(\n agent,\n () => backgroundWork.inspect(agent.context.sessionId),\n undefined,\n diagnostics,\n );\n return {\n value: watch,\n services: [backgroundWork.plugin],\n onDispose: async () => {\n watch.dispose();\n await diagnostics.dispose();\n },\n };\n});\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'agent_settled')!,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'agent_start')!,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'session_shutdown')!,\n);\n","import { defineHook, type WithRoot } from '@agimon-ai/po-core/extensionFile';\nimport type { DoomServerPluginContext } from '@agimon-ai/po-core/serverFacet';\n\ntype AutoStopServerScope = Awaited<ReturnType<typeof import('../root.server').default>>['value'];\nexport default defineHook((context: WithRoot<DoomServerPluginContext, AutoStopServerScope>) =>\n context.root.hooks.find((hook) => hook.event === 'session_tree')!,\n);\n","// Generated by @agimon-ai/po-build. Do not edit by hand.\nimport { defineServerPlugin, type DoomServerSessionPlugin } from '@agimon-ai/po-core/serverFacet';\nimport { composeServerProviderOverrides } from '@agimon-ai/po-core/serverFacet';\nimport { composeRootHooks } from '@agimon-ai/po-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.server';\nimport hookAgentSettled from '../src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server';\nimport hookAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/agent-start.server';\nimport hookSessionShutdown from '../src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server';\nimport hookSessionTree from '../src/extensions/workspaces/sessions/(backend)/hook/session-tree.server';\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\nconst via = <T>(identity: Record<string, unknown>, value: unknown): T =>\n (value === undefined ? undefined : { ...identity, ...(value as object) }) as T;\n\nexport const facet = defineServerPlugin({\n name: '@agimon-ai/po-autostop',\n providerOverrides: composeServerProviderOverrides(rootSession),\n session: async (context) => {\n const scopeSession = await rootSession(context);\n const contextSession = { ...context, root: scopeSession.value };\n return {\n ...composeRootHooks(scopeSession),\n get hooks(): DoomServerSessionPlugin['hooks'] { return [via({ event: 'agent_settled' }, at(hookAgentSettled, contextSession)), via({ event: 'agent_start' }, at(hookAgentStart, contextSession)), via({ event: 'session_shutdown' }, at(hookSessionShutdown, contextSession)), via({ event: 'session_tree' }, at(hookSessionTree, contextSession))]; },\n services: [...(scopeSession.services ?? [])],\n get activities(): DoomServerSessionPlugin['activities'] { return [...(scopeSession.activities ?? [])]; },\n };\n },\n});\n\nexport default facet;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
//#region src/constants/idlePolicy.ts
|
|
2
|
+
const DEFAULT_AUTO_STOP_DELAYS = {
|
|
3
|
+
cooldownMs: 5e3,
|
|
4
|
+
recheckMs: 100
|
|
5
|
+
};
|
|
6
|
+
const AUTO_STOP_ACTION = {
|
|
7
|
+
/** The session has nothing left to do and can exit. */
|
|
8
|
+
shutdown: "shutdown",
|
|
9
|
+
/** Look again after the returned delay. */
|
|
10
|
+
recheck: "recheck",
|
|
11
|
+
/** The user is back; stop watching until the agent settles again. */
|
|
12
|
+
standDown: "stand-down"
|
|
13
|
+
};
|
|
14
|
+
/** Telemetry events that explain an auto-stop decision after the fact. */
|
|
15
|
+
const AUTO_STOP_EVENT = {
|
|
16
|
+
/** Idle, but background work is still listed; carries what it is. */
|
|
17
|
+
waitingOnBackgroundWork: "doom_autostop.waiting_on_background_work",
|
|
18
|
+
/** A message was queued, so the watch stopped until the agent settles again. */
|
|
19
|
+
stoodDown: "doom_autostop.stood_down",
|
|
20
|
+
/** Everything was clear and shutdown was requested. */
|
|
21
|
+
shutdownRequested: "doom_autostop.shutdown_requested"
|
|
22
|
+
};
|
|
23
|
+
/** How often a long wait on background work is reported again. */
|
|
24
|
+
const AUTO_STOP_REPORT_INTERVAL_MS = 6e4;
|
|
25
|
+
/** Longest the session waits for telemetry to flush on its way out. */
|
|
26
|
+
const AUTO_STOP_TELEMETRY_FLUSH_MS = 2e3;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/services/idlePolicy/index.ts
|
|
29
|
+
/**
|
|
30
|
+
* Taken when the agent reports it has settled.
|
|
31
|
+
*
|
|
32
|
+
* Settling is not enough on its own: a queued message means the session is
|
|
33
|
+
* about to run again, and even an empty queue gets the cooldown so a user
|
|
34
|
+
* typing their next prompt is not cut off mid-word.
|
|
35
|
+
*/
|
|
36
|
+
function decideOnSettled(activity, delays) {
|
|
37
|
+
if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };
|
|
38
|
+
return {
|
|
39
|
+
action: AUTO_STOP_ACTION.recheck,
|
|
40
|
+
delayMs: delays.cooldownMs
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Taken when a scheduled look comes due.
|
|
45
|
+
*
|
|
46
|
+
* The agent can still be streaming after it settled, so a session that is not
|
|
47
|
+
* yet idle is polled rather than stopped.
|
|
48
|
+
*/
|
|
49
|
+
function decideOnRecheck(activity, delays) {
|
|
50
|
+
if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };
|
|
51
|
+
if (activity.isIdle) return { action: AUTO_STOP_ACTION.shutdown };
|
|
52
|
+
return {
|
|
53
|
+
action: AUTO_STOP_ACTION.recheck,
|
|
54
|
+
delayMs: delays.recheckMs
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { AUTO_STOP_REPORT_INTERVAL_MS as a, AUTO_STOP_EVENT as i, decideOnSettled as n, AUTO_STOP_TELEMETRY_FLUSH_MS as o, AUTO_STOP_ACTION as r, DEFAULT_AUTO_STOP_DELAYS as s, decideOnRecheck as t };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=idlePolicy-BNLdwumq.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idlePolicy-BNLdwumq.mjs","names":[],"sources":["../src/constants/idlePolicy.ts","../src/services/idlePolicy/index.ts"],"sourcesContent":["export const DEFAULT_AUTO_STOP_DELAYS = {\n cooldownMs: 5_000,\n recheckMs: 100,\n};\n\nexport const AUTO_STOP_ACTION = {\n /** The session has nothing left to do and can exit. */\n shutdown: 'shutdown',\n /** Look again after the returned delay. */\n recheck: 'recheck',\n /** The user is back; stop watching until the agent settles again. */\n standDown: 'stand-down',\n} as const;\n\n/** Telemetry events that explain an auto-stop decision after the fact. */\nexport const AUTO_STOP_EVENT = {\n /** Idle, but background work is still listed; carries what it is. */\n waitingOnBackgroundWork: 'doom_autostop.waiting_on_background_work',\n /** A message was queued, so the watch stopped until the agent settles again. */\n stoodDown: 'doom_autostop.stood_down',\n /** Everything was clear and shutdown was requested. */\n shutdownRequested: 'doom_autostop.shutdown_requested',\n} as const;\n\n/** How often a long wait on background work is reported again. */\nexport const AUTO_STOP_REPORT_INTERVAL_MS = 60_000;\n/** Longest the session waits for telemetry to flush on its way out. */\nexport const AUTO_STOP_TELEMETRY_FLUSH_MS = 2_000;\n/** Longest list of work items or errors carried on one event. */\nexport const AUTO_STOP_DETAIL_MAX_LENGTH = 500;\n","import { AUTO_STOP_ACTION } from '../../constants/idlePolicy';\nimport type { AutoStopDecision, AutoStopDelays, SessionActivity } from './type';\n/**\n * Taken when the agent reports it has settled.\n *\n * Settling is not enough on its own: a queued message means the session is\n * about to run again, and even an empty queue gets the cooldown so a user\n * typing their next prompt is not cut off mid-word.\n */\nexport function decideOnSettled(activity: SessionActivity, delays: AutoStopDelays): AutoStopDecision {\n if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };\n return { action: AUTO_STOP_ACTION.recheck, delayMs: delays.cooldownMs };\n}\n\n/**\n * Taken when a scheduled look comes due.\n *\n * The agent can still be streaming after it settled, so a session that is not\n * yet idle is polled rather than stopped.\n */\nexport function decideOnRecheck(activity: SessionActivity, delays: AutoStopDelays): AutoStopDecision {\n if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };\n if (activity.isIdle) return { action: AUTO_STOP_ACTION.shutdown };\n return { action: AUTO_STOP_ACTION.recheck, delayMs: delays.recheckMs };\n}\n"],"mappings":";AAAA,MAAa,2BAA2B;CACtC,YAAY;CACZ,WAAW;AACb;AAEA,MAAa,mBAAmB;;CAE9B,UAAU;;CAEV,SAAS;;CAET,WAAW;AACb;;AAGA,MAAa,kBAAkB;;CAE7B,yBAAyB;;CAEzB,WAAW;;CAEX,mBAAmB;AACrB;;AAGA,MAAa,+BAA+B;;AAE5C,MAAa,+BAA+B;;;;;;;;;;AClB5C,SAAgB,gBAAgB,UAA2B,QAA0C;CACnG,IAAI,SAAS,oBAAoB,OAAO,EAAE,QAAQ,iBAAiB,UAAU;CAC7E,OAAO;EAAE,QAAQ,iBAAiB;EAAS,SAAS,OAAO;CAAW;AACxE;;;;;;;AAQA,SAAgB,gBAAgB,UAA2B,QAA0C;CACnG,IAAI,SAAS,oBAAoB,OAAO,EAAE,QAAQ,iBAAiB,UAAU;CAC7E,IAAI,SAAS,QAAQ,OAAO,EAAE,QAAQ,iBAAiB,SAAS;CAChE,OAAO;EAAE,QAAQ,iBAAiB;EAAS,SAAS,OAAO;CAAU;AACvE"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//#region src/constants/idlePolicy.ts
|
|
2
|
+
const DEFAULT_AUTO_STOP_DELAYS = {
|
|
3
|
+
cooldownMs: 5e3,
|
|
4
|
+
recheckMs: 100
|
|
5
|
+
};
|
|
6
|
+
const AUTO_STOP_ACTION = {
|
|
7
|
+
/** The session has nothing left to do and can exit. */
|
|
8
|
+
shutdown: "shutdown",
|
|
9
|
+
/** Look again after the returned delay. */
|
|
10
|
+
recheck: "recheck",
|
|
11
|
+
/** The user is back; stop watching until the agent settles again. */
|
|
12
|
+
standDown: "stand-down"
|
|
13
|
+
};
|
|
14
|
+
/** Telemetry events that explain an auto-stop decision after the fact. */
|
|
15
|
+
const AUTO_STOP_EVENT = {
|
|
16
|
+
/** Idle, but background work is still listed; carries what it is. */
|
|
17
|
+
waitingOnBackgroundWork: "doom_autostop.waiting_on_background_work",
|
|
18
|
+
/** A message was queued, so the watch stopped until the agent settles again. */
|
|
19
|
+
stoodDown: "doom_autostop.stood_down",
|
|
20
|
+
/** Everything was clear and shutdown was requested. */
|
|
21
|
+
shutdownRequested: "doom_autostop.shutdown_requested"
|
|
22
|
+
};
|
|
23
|
+
/** How often a long wait on background work is reported again. */
|
|
24
|
+
const AUTO_STOP_REPORT_INTERVAL_MS = 6e4;
|
|
25
|
+
/** Longest the session waits for telemetry to flush on its way out. */
|
|
26
|
+
const AUTO_STOP_TELEMETRY_FLUSH_MS = 2e3;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/services/idlePolicy/index.ts
|
|
29
|
+
/**
|
|
30
|
+
* Taken when the agent reports it has settled.
|
|
31
|
+
*
|
|
32
|
+
* Settling is not enough on its own: a queued message means the session is
|
|
33
|
+
* about to run again, and even an empty queue gets the cooldown so a user
|
|
34
|
+
* typing their next prompt is not cut off mid-word.
|
|
35
|
+
*/
|
|
36
|
+
function decideOnSettled(activity, delays) {
|
|
37
|
+
if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };
|
|
38
|
+
return {
|
|
39
|
+
action: AUTO_STOP_ACTION.recheck,
|
|
40
|
+
delayMs: delays.cooldownMs
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Taken when a scheduled look comes due.
|
|
45
|
+
*
|
|
46
|
+
* The agent can still be streaming after it settled, so a session that is not
|
|
47
|
+
* yet idle is polled rather than stopped.
|
|
48
|
+
*/
|
|
49
|
+
function decideOnRecheck(activity, delays) {
|
|
50
|
+
if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };
|
|
51
|
+
if (activity.isIdle) return { action: AUTO_STOP_ACTION.shutdown };
|
|
52
|
+
return {
|
|
53
|
+
action: AUTO_STOP_ACTION.recheck,
|
|
54
|
+
delayMs: delays.recheckMs
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
Object.defineProperty(exports, "AUTO_STOP_ACTION", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
get: function() {
|
|
61
|
+
return AUTO_STOP_ACTION;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
Object.defineProperty(exports, "AUTO_STOP_EVENT", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function() {
|
|
67
|
+
return AUTO_STOP_EVENT;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(exports, "AUTO_STOP_REPORT_INTERVAL_MS", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
get: function() {
|
|
73
|
+
return AUTO_STOP_REPORT_INTERVAL_MS;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(exports, "AUTO_STOP_TELEMETRY_FLUSH_MS", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
get: function() {
|
|
79
|
+
return AUTO_STOP_TELEMETRY_FLUSH_MS;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
Object.defineProperty(exports, "DEFAULT_AUTO_STOP_DELAYS", {
|
|
83
|
+
enumerable: true,
|
|
84
|
+
get: function() {
|
|
85
|
+
return DEFAULT_AUTO_STOP_DELAYS;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
Object.defineProperty(exports, "decideOnRecheck", {
|
|
89
|
+
enumerable: true,
|
|
90
|
+
get: function() {
|
|
91
|
+
return decideOnRecheck;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
Object.defineProperty(exports, "decideOnSettled", {
|
|
95
|
+
enumerable: true,
|
|
96
|
+
get: function() {
|
|
97
|
+
return decideOnSettled;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=idlePolicy-Dw8blgEH.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"idlePolicy-Dw8blgEH.cjs","names":[],"sources":["../src/constants/idlePolicy.ts","../src/services/idlePolicy/index.ts"],"sourcesContent":["export const DEFAULT_AUTO_STOP_DELAYS = {\n cooldownMs: 5_000,\n recheckMs: 100,\n};\n\nexport const AUTO_STOP_ACTION = {\n /** The session has nothing left to do and can exit. */\n shutdown: 'shutdown',\n /** Look again after the returned delay. */\n recheck: 'recheck',\n /** The user is back; stop watching until the agent settles again. */\n standDown: 'stand-down',\n} as const;\n\n/** Telemetry events that explain an auto-stop decision after the fact. */\nexport const AUTO_STOP_EVENT = {\n /** Idle, but background work is still listed; carries what it is. */\n waitingOnBackgroundWork: 'doom_autostop.waiting_on_background_work',\n /** A message was queued, so the watch stopped until the agent settles again. */\n stoodDown: 'doom_autostop.stood_down',\n /** Everything was clear and shutdown was requested. */\n shutdownRequested: 'doom_autostop.shutdown_requested',\n} as const;\n\n/** How often a long wait on background work is reported again. */\nexport const AUTO_STOP_REPORT_INTERVAL_MS = 60_000;\n/** Longest the session waits for telemetry to flush on its way out. */\nexport const AUTO_STOP_TELEMETRY_FLUSH_MS = 2_000;\n/** Longest list of work items or errors carried on one event. */\nexport const AUTO_STOP_DETAIL_MAX_LENGTH = 500;\n","import { AUTO_STOP_ACTION } from '../../constants/idlePolicy';\nimport type { AutoStopDecision, AutoStopDelays, SessionActivity } from './type';\n/**\n * Taken when the agent reports it has settled.\n *\n * Settling is not enough on its own: a queued message means the session is\n * about to run again, and even an empty queue gets the cooldown so a user\n * typing their next prompt is not cut off mid-word.\n */\nexport function decideOnSettled(activity: SessionActivity, delays: AutoStopDelays): AutoStopDecision {\n if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };\n return { action: AUTO_STOP_ACTION.recheck, delayMs: delays.cooldownMs };\n}\n\n/**\n * Taken when a scheduled look comes due.\n *\n * The agent can still be streaming after it settled, so a session that is not\n * yet idle is polled rather than stopped.\n */\nexport function decideOnRecheck(activity: SessionActivity, delays: AutoStopDelays): AutoStopDecision {\n if (activity.hasPendingMessages) return { action: AUTO_STOP_ACTION.standDown };\n if (activity.isIdle) return { action: AUTO_STOP_ACTION.shutdown };\n return { action: AUTO_STOP_ACTION.recheck, delayMs: delays.recheckMs };\n}\n"],"mappings":";AAAA,MAAa,2BAA2B;CACtC,YAAY;CACZ,WAAW;AACb;AAEA,MAAa,mBAAmB;;CAE9B,UAAU;;CAEV,SAAS;;CAET,WAAW;AACb;;AAGA,MAAa,kBAAkB;;CAE7B,yBAAyB;;CAEzB,WAAW;;CAEX,mBAAmB;AACrB;;AAGA,MAAa,+BAA+B;;AAE5C,MAAa,+BAA+B;;;;;;;;;;AClB5C,SAAgB,gBAAgB,UAA2B,QAA0C;CACnG,IAAI,SAAS,oBAAoB,OAAO,EAAE,QAAQ,iBAAiB,UAAU;CAC7E,OAAO;EAAE,QAAQ,iBAAiB;EAAS,SAAS,OAAO;CAAW;AACxE;;;;;;;AAQA,SAAgB,gBAAgB,UAA2B,QAA0C;CACnG,IAAI,SAAS,oBAAoB,OAAO,EAAE,QAAQ,iBAAiB,UAAU;CAC7E,IAAI,SAAS,QAAQ,OAAO,EAAE,QAAQ,iBAAiB,SAAS;CAChE,OAAO;EAAE,QAAQ,iBAAiB;EAAS,SAAS,OAAO;CAAU;AACvE"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_idlePolicy = require("./idlePolicy-Dw8blgEH.cjs");
|
|
3
|
+
exports.AUTO_STOP_ACTION = require_idlePolicy.AUTO_STOP_ACTION;
|
|
4
|
+
exports.DEFAULT_AUTO_STOP_DELAYS = require_idlePolicy.DEFAULT_AUTO_STOP_DELAYS;
|
|
5
|
+
exports.decideOnRecheck = require_idlePolicy.decideOnRecheck;
|
|
6
|
+
exports.decideOnSettled = require_idlePolicy.decideOnSettled;
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/constants/idlePolicy.d.ts
|
|
2
|
+
export declare const DEFAULT_AUTO_STOP_DELAYS: {
|
|
3
|
+
cooldownMs: number;
|
|
4
|
+
recheckMs: number;
|
|
5
|
+
};
|
|
6
|
+
export declare const AUTO_STOP_ACTION: {
|
|
7
|
+
/** The session has nothing left to do and can exit. */
|
|
8
|
+
readonly shutdown: 'shutdown';
|
|
9
|
+
/** Look again after the returned delay. */
|
|
10
|
+
readonly recheck: 'recheck';
|
|
11
|
+
/** The user is back; stop watching until the agent settles again. */
|
|
12
|
+
readonly standDown: 'stand-down';
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/services/idlePolicy/type.d.ts
|
|
16
|
+
/** What the session was doing the moment the policy was asked. */
|
|
17
|
+
interface SessionActivity {
|
|
18
|
+
readonly hasPendingMessages: boolean;
|
|
19
|
+
readonly isIdle: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** How long the policy waits before each look at the session. */
|
|
22
|
+
interface AutoStopDelays {
|
|
23
|
+
/** Grace period after the agent settles, so a follow-up prompt still lands. */
|
|
24
|
+
readonly cooldownMs: number;
|
|
25
|
+
/** Gap between looks while the agent is settled but still streaming. */
|
|
26
|
+
readonly recheckMs: number;
|
|
27
|
+
}
|
|
28
|
+
type AutoStopAction = (typeof AUTO_STOP_ACTION)[keyof typeof AUTO_STOP_ACTION];
|
|
29
|
+
type AutoStopDecision = {
|
|
30
|
+
readonly action: typeof AUTO_STOP_ACTION.shutdown;
|
|
31
|
+
} | {
|
|
32
|
+
readonly action: typeof AUTO_STOP_ACTION.recheck;
|
|
33
|
+
readonly delayMs: number;
|
|
34
|
+
} | {
|
|
35
|
+
readonly action: typeof AUTO_STOP_ACTION.standDown;
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/services/idlePolicy/index.d.ts
|
|
39
|
+
/**
|
|
40
|
+
* Taken when the agent reports it has settled.
|
|
41
|
+
*
|
|
42
|
+
* Settling is not enough on its own: a queued message means the session is
|
|
43
|
+
* about to run again, and even an empty queue gets the cooldown so a user
|
|
44
|
+
* typing their next prompt is not cut off mid-word.
|
|
45
|
+
*/
|
|
46
|
+
export declare function decideOnSettled(activity: SessionActivity, delays: AutoStopDelays): AutoStopDecision;
|
|
47
|
+
/**
|
|
48
|
+
* Taken when a scheduled look comes due.
|
|
49
|
+
*
|
|
50
|
+
* The agent can still be streaming after it settled, so a session that is not
|
|
51
|
+
* yet idle is polled rather than stopped.
|
|
52
|
+
*/
|
|
53
|
+
export declare function decideOnRecheck(activity: SessionActivity, delays: AutoStopDelays): AutoStopDecision;
|
|
54
|
+
//#endregion
|
|
55
|
+
export type { AutoStopAction, AutoStopDecision, AutoStopDelays, SessionActivity };
|
|
56
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/constants/idlePolicy.ts","../src/services/idlePolicy/type.ts","../src/services/idlePolicy/index.ts"],"mappings":";qBAAa;EACX;EACA;;qBAGW;;WAEX;;WAEA;;WAEA;;;;;UCTe;WACN;WACA;;;UAIM;;WAEN;;WAEA;;KAGC,yBAAyB,+BAA+B;KAExD;WACG,eAAe,iBAAiB;;WAChC,eAAe,iBAAiB;WAAkB;;WAClD,eAAe,iBAAiB;;;;;;;;;;;wBCX/B,gBAAgB,UAAU,iBAAiB,QAAQ,iBAAiB;;;;;;;wBAWpE,gBAAgB,UAAU,iBAAiB,QAAQ,iBAAiB"}
|
package/dist/index.mjs
ADDED
package/llms.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Po Autostop
|
|
2
|
+
|
|
3
|
+
Shuts a Po session down once the agent settles and stays idle. Shares core's holding rule with workflow supervision: runners awaiting completion admission and owned subagents keep the session open.
|
|
4
|
+
|
|
5
|
+
## Resources
|
|
6
|
+
|
|
7
|
+
- [Package README](./README.md): the idle policy, the cooldown, and when the session is left alone.
|
package/package.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agimon-ai/po-autostop",
|
|
3
|
+
"version": "0.0.0-alpha.0",
|
|
4
|
+
"description": "Shuts a Po session down once the agent settles and stays idle.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ai",
|
|
7
|
+
"auto-stop",
|
|
8
|
+
"coding-agent",
|
|
9
|
+
"developer-tools",
|
|
10
|
+
"idle-timeout",
|
|
11
|
+
"pi-coding-agent",
|
|
12
|
+
"pi-extension",
|
|
13
|
+
"pi-package",
|
|
14
|
+
"po",
|
|
15
|
+
"session-shutdown"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://agimon.ai",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/AgiFlow/po/issues"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Vuong Ngo",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/AgiFlow/po.git",
|
|
26
|
+
"directory": "packages/foundations/po-autostop"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"llms.txt",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"package.json"
|
|
34
|
+
],
|
|
35
|
+
"type": "module",
|
|
36
|
+
"main": "./dist/index.cjs",
|
|
37
|
+
"module": "./dist/index.mjs",
|
|
38
|
+
"types": "./dist/index.d.mts",
|
|
39
|
+
"jsnext:main": "./dist/index.mjs",
|
|
40
|
+
"exports": {
|
|
41
|
+
".": {
|
|
42
|
+
"types": "./dist/index.d.mts",
|
|
43
|
+
"import": "./dist/index.mjs",
|
|
44
|
+
"require": "./dist/index.cjs"
|
|
45
|
+
},
|
|
46
|
+
"./apiContracts": {
|
|
47
|
+
"types": "./dist/apiContracts.d.mts",
|
|
48
|
+
"import": "./dist/apiContracts.mjs",
|
|
49
|
+
"require": "./dist/apiContracts.cjs"
|
|
50
|
+
},
|
|
51
|
+
"./extensions/server": {
|
|
52
|
+
"types": "./dist/extensions/server.d.mts",
|
|
53
|
+
"import": "./dist/extensions/server.mjs",
|
|
54
|
+
"require": "./dist/extensions/server.cjs"
|
|
55
|
+
},
|
|
56
|
+
"./package.json": "./package.json"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"@agimon-ai/po-core": "0.0.0-alpha.0",
|
|
63
|
+
"@agimon-ai/po-telemetry": "0.0.0-alpha.0",
|
|
64
|
+
"@deepseek-ai/cordis": "4.0.4"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@agimon-ai/po-build": "0.0.0-alpha.0",
|
|
68
|
+
"@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.0-alpha.0",
|
|
69
|
+
"@earendil-works/pi-coding-agent": "1.0.2",
|
|
70
|
+
"@types/node": "26.6.4",
|
|
71
|
+
"@vitest/coverage-v8": "5.0.3",
|
|
72
|
+
"tsdown": "0.23.0",
|
|
73
|
+
"typescript": "7.0.2",
|
|
74
|
+
"vitest": "5.0.3"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@earendil-works/pi-coding-agent": "1.0.2"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"@earendil-works/pi-coding-agent": {
|
|
81
|
+
"optional": true
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">=24"
|
|
86
|
+
},
|
|
87
|
+
"poServer": {
|
|
88
|
+
"entry": "./generated/server.ts",
|
|
89
|
+
"dist": "./dist/extensions/server.mjs",
|
|
90
|
+
"scopes": [
|
|
91
|
+
"session"
|
|
92
|
+
],
|
|
93
|
+
"contracts": {
|
|
94
|
+
"entry": "./src/exports/apiContracts.ts",
|
|
95
|
+
"dist": "./dist/apiContracts.mjs"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"scripts": {
|
|
99
|
+
"build": "tsdown",
|
|
100
|
+
"test": "vitest --run",
|
|
101
|
+
"typecheck": "tsc --noEmit",
|
|
102
|
+
"lint": "oxlint . && oxfmt . --check",
|
|
103
|
+
"fixcode": "oxlint . --fix && oxfmt ."
|
|
104
|
+
}
|
|
105
|
+
}
|