@chozzz/vargos 3.1.4 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +2 -3
- package/README.md +25 -34
- package/dist/.templates/agent/skills/skill-creator/SKILL.md +204 -32
- package/dist/.templates/workspace/AGENTS.md +32 -0
- package/dist/boot.d.ts +5 -0
- package/dist/boot.d.ts.map +1 -1
- package/dist/boot.js +62 -87
- package/dist/boot.js.map +1 -1
- package/dist/cli/channels.d.ts +11 -17
- package/dist/cli/channels.d.ts.map +1 -1
- package/dist/cli/channels.js +25 -76
- package/dist/cli/channels.js.map +1 -1
- package/dist/cli/chat.d.ts +6 -0
- package/dist/cli/chat.d.ts.map +1 -0
- package/dist/cli/chat.js +49 -0
- package/dist/cli/chat.js.map +1 -0
- package/dist/cli/onboard.d.ts.map +1 -1
- package/dist/cli/onboard.js +2 -8
- package/dist/cli/onboard.js.map +1 -1
- package/dist/cli.d.ts +9 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +206 -264
- package/dist/cli.js.map +1 -1
- package/dist/core/bus.d.ts +32 -0
- package/dist/core/bus.d.ts.map +1 -0
- package/dist/core/bus.js +133 -0
- package/dist/core/bus.js.map +1 -0
- package/dist/core/cli.d.ts +38 -0
- package/dist/core/cli.d.ts.map +1 -0
- package/dist/core/cli.js +199 -0
- package/dist/core/cli.js.map +1 -0
- package/dist/core/errors.d.ts +21 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +30 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/loader.d.ts +31 -0
- package/dist/core/loader.d.ts.map +1 -0
- package/dist/core/loader.js +73 -0
- package/dist/core/loader.js.map +1 -0
- package/dist/core/local.d.ts +12 -0
- package/dist/core/local.d.ts.map +1 -0
- package/dist/core/local.js +23 -0
- package/dist/core/local.js.map +1 -0
- package/dist/core/rpc-server.d.ts +11 -0
- package/dist/core/rpc-server.d.ts.map +1 -0
- package/dist/core/rpc-server.js +69 -0
- package/dist/core/rpc-server.js.map +1 -0
- package/dist/core/services.d.ts +8 -0
- package/dist/core/services.d.ts.map +1 -0
- package/dist/core/services.js +33 -0
- package/dist/core/services.js.map +1 -0
- package/dist/core/types.d.ts +63 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/edge/mcp/index.d.ts +11 -11
- package/dist/edge/mcp/index.d.ts.map +1 -1
- package/dist/edge/mcp/index.js +24 -28
- package/dist/edge/mcp/index.js.map +1 -1
- package/dist/edge/webhooks/index.d.ts +8 -14
- package/dist/edge/webhooks/index.d.ts.map +1 -1
- package/dist/edge/webhooks/index.js +140 -194
- package/dist/edge/webhooks/index.js.map +1 -1
- package/dist/lib/logger.d.ts +2 -3
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +1 -1
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/paginate.d.ts +5 -1
- package/dist/lib/paginate.d.ts.map +1 -1
- package/dist/lib/retry.js +1 -1
- package/dist/lib/retry.js.map +1 -1
- package/dist/lib/util.d.ts +16 -0
- package/dist/lib/util.d.ts.map +1 -0
- package/dist/lib/util.js +63 -0
- package/dist/lib/util.js.map +1 -0
- package/dist/scripts/verify-core.d.ts +8 -0
- package/dist/scripts/verify-core.d.ts.map +1 -0
- package/dist/scripts/verify-core.js +191 -0
- package/dist/scripts/verify-core.js.map +1 -0
- package/dist/services/agent/directives.d.ts +2 -0
- package/dist/services/agent/directives.d.ts.map +1 -1
- package/dist/services/agent/directives.js +16 -1
- package/dist/services/agent/directives.js.map +1 -1
- package/dist/services/agent/index.d.ts +34 -21
- package/dist/services/agent/index.d.ts.map +1 -1
- package/dist/services/agent/index.js +470 -468
- package/dist/services/agent/index.js.map +1 -1
- package/dist/services/agent/tools.d.ts +3 -3
- package/dist/services/agent/tools.d.ts.map +1 -1
- package/dist/services/agent/tools.js +11 -19
- package/dist/services/agent/tools.js.map +1 -1
- package/dist/services/agent/types.d.ts +1 -1
- package/dist/services/agent/types.d.ts.map +1 -1
- package/dist/services/{channels → channel}/base-adapter.d.ts +5 -6
- package/dist/services/channel/base-adapter.d.ts.map +1 -0
- package/dist/services/channel/base-adapter.js.map +1 -0
- package/dist/services/channel/debounce.d.ts.map +1 -0
- package/dist/services/channel/debounce.js.map +1 -0
- package/dist/services/channel/dedupe.d.ts.map +1 -0
- package/dist/services/channel/dedupe.js.map +1 -0
- package/dist/services/channel/delivery.d.ts.map +1 -0
- package/dist/services/{channels → channel}/delivery.js +1 -1
- package/dist/services/channel/delivery.js.map +1 -0
- package/dist/services/{channels → channel}/index.d.ts +14 -17
- package/dist/services/channel/index.d.ts.map +1 -0
- package/dist/services/channel/index.js +354 -0
- package/dist/services/channel/index.js.map +1 -0
- package/dist/services/channel/link-expand.d.ts.map +1 -0
- package/dist/services/channel/link-expand.js.map +1 -0
- package/dist/services/channel/media-paths.d.ts.map +1 -0
- package/dist/services/channel/media-paths.js.map +1 -0
- package/dist/services/{channels → channel}/pipeline.d.ts +1 -1
- package/dist/services/channel/pipeline.d.ts.map +1 -0
- package/dist/services/channel/pipeline.js.map +1 -0
- package/dist/services/channel/provider-loader.d.ts.map +1 -0
- package/dist/services/channel/provider-loader.js.map +1 -0
- package/dist/services/channel/providers/telegram/adapter.d.ts.map +1 -0
- package/dist/services/{channels → channel}/providers/telegram/adapter.js +1 -1
- package/dist/services/channel/providers/telegram/adapter.js.map +1 -0
- package/dist/services/channel/providers/telegram/index.d.ts.map +1 -0
- package/dist/services/channel/providers/telegram/index.js.map +1 -0
- package/dist/services/channel/providers/telegram/normalizer.d.ts.map +1 -0
- package/dist/services/channel/providers/telegram/normalizer.js.map +1 -0
- package/dist/services/channel/providers/telegram/types.d.ts.map +1 -0
- package/dist/services/channel/providers/telegram/types.js.map +1 -0
- package/dist/services/channel/providers/whatsapp/adapter.d.ts.map +1 -0
- package/dist/services/channel/providers/whatsapp/adapter.js.map +1 -0
- package/dist/services/channel/providers/whatsapp/index.d.ts.map +1 -0
- package/dist/services/channel/providers/whatsapp/index.js.map +1 -0
- package/dist/services/channel/providers/whatsapp/normalizer.d.ts.map +1 -0
- package/dist/services/channel/providers/whatsapp/normalizer.js.map +1 -0
- package/dist/services/channel/providers/whatsapp/session.d.ts.map +1 -0
- package/dist/services/channel/providers/whatsapp/session.js.map +1 -0
- package/dist/services/channel/providers/whatsapp/types.d.ts.map +1 -0
- package/dist/services/channel/providers/whatsapp/types.js.map +1 -0
- package/dist/services/channel/reconnect.d.ts.map +1 -0
- package/dist/services/channel/reconnect.js.map +1 -0
- package/dist/services/channel/status-reactions.d.ts.map +1 -0
- package/dist/services/channel/status-reactions.js.map +1 -0
- package/dist/services/{channels → channel}/types.d.ts +1 -1
- package/dist/services/channel/types.d.ts.map +1 -0
- package/dist/services/channel/types.js.map +1 -0
- package/dist/services/channel/typing-state.d.ts.map +1 -0
- package/dist/services/channel/typing-state.js.map +1 -0
- package/dist/services/config/index.d.ts +45 -46
- package/dist/services/config/index.d.ts.map +1 -1
- package/dist/services/config/index.js +89 -182
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/schemas/cron.d.ts +3 -3
- package/dist/services/config/schemas/providers.d.ts +12 -12
- package/dist/services/config/schemas/webhooks.d.ts +2 -2
- package/dist/services/cron/index.d.ts +16 -19
- package/dist/services/cron/index.d.ts.map +1 -1
- package/dist/services/cron/index.js +340 -396
- package/dist/services/cron/index.js.map +1 -1
- package/dist/services/log/index.d.ts +9 -8
- package/dist/services/log/index.d.ts.map +1 -1
- package/dist/services/log/index.js +71 -123
- package/dist/services/log/index.js.map +1 -1
- package/dist/services/{mcp-client → mcp}/index.d.ts +9 -11
- package/dist/services/mcp/index.d.ts.map +1 -0
- package/dist/services/{mcp-client → mcp}/index.js +19 -35
- package/dist/services/mcp/index.js.map +1 -0
- package/dist/services/media/index.d.ts +9 -13
- package/dist/services/media/index.d.ts.map +1 -1
- package/dist/services/media/index.js +53 -105
- package/dist/services/media/index.js.map +1 -1
- package/dist/services/memory/index.d.ts +12 -18
- package/dist/services/memory/index.d.ts.map +1 -1
- package/dist/services/memory/index.js +70 -132
- package/dist/services/memory/index.js.map +1 -1
- package/dist/services/web/index.d.ts +7 -7
- package/dist/services/web/index.d.ts.map +1 -1
- package/dist/services/web/index.js +41 -86
- package/dist/services/web/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/gateway/bus.d.ts +0 -50
- package/dist/gateway/bus.d.ts.map +0 -1
- package/dist/gateway/bus.js +0 -2
- package/dist/gateway/bus.js.map +0 -1
- package/dist/gateway/decorators.d.ts +0 -40
- package/dist/gateway/decorators.d.ts.map +0 -1
- package/dist/gateway/decorators.js +0 -43
- package/dist/gateway/decorators.js.map +0 -1
- package/dist/gateway/emitter.d.ts +0 -52
- package/dist/gateway/emitter.d.ts.map +0 -1
- package/dist/gateway/emitter.js +0 -304
- package/dist/gateway/emitter.js.map +0 -1
- package/dist/gateway/events.d.ts +0 -314
- package/dist/gateway/events.d.ts.map +0 -1
- package/dist/gateway/events.js +0 -7
- package/dist/gateway/events.js.map +0 -1
- package/dist/gateway/tcp-server.d.ts +0 -7
- package/dist/gateway/tcp-server.d.ts.map +0 -1
- package/dist/gateway/tcp-server.js +0 -118
- package/dist/gateway/tcp-server.js.map +0 -1
- package/dist/lib/id.d.ts +0 -3
- package/dist/lib/id.d.ts.map +0 -1
- package/dist/lib/id.js +0 -5
- package/dist/lib/id.js.map +0 -1
- package/dist/lib/sleep.d.ts +0 -6
- package/dist/lib/sleep.d.ts.map +0 -1
- package/dist/lib/sleep.js +0 -22
- package/dist/lib/sleep.js.map +0 -1
- package/dist/lib/strip-markdown.d.ts +0 -7
- package/dist/lib/strip-markdown.d.ts.map +0 -1
- package/dist/lib/strip-markdown.js +0 -32
- package/dist/lib/strip-markdown.js.map +0 -1
- package/dist/lib/timeout.d.ts +0 -6
- package/dist/lib/timeout.d.ts.map +0 -1
- package/dist/lib/timeout.js +0 -11
- package/dist/lib/timeout.js.map +0 -1
- package/dist/lib/truncate.d.ts +0 -11
- package/dist/lib/truncate.d.ts.map +0 -1
- package/dist/lib/truncate.js +0 -17
- package/dist/lib/truncate.js.map +0 -1
- package/dist/services/channels/base-adapter.d.ts.map +0 -1
- package/dist/services/channels/base-adapter.js.map +0 -1
- package/dist/services/channels/debounce.d.ts.map +0 -1
- package/dist/services/channels/debounce.js.map +0 -1
- package/dist/services/channels/dedupe.d.ts.map +0 -1
- package/dist/services/channels/dedupe.js.map +0 -1
- package/dist/services/channels/delivery.d.ts.map +0 -1
- package/dist/services/channels/delivery.js.map +0 -1
- package/dist/services/channels/index.d.ts.map +0 -1
- package/dist/services/channels/index.js +0 -413
- package/dist/services/channels/index.js.map +0 -1
- package/dist/services/channels/link-expand.d.ts.map +0 -1
- package/dist/services/channels/link-expand.js.map +0 -1
- package/dist/services/channels/media-paths.d.ts.map +0 -1
- package/dist/services/channels/media-paths.js.map +0 -1
- package/dist/services/channels/pipeline.d.ts.map +0 -1
- package/dist/services/channels/pipeline.js.map +0 -1
- package/dist/services/channels/provider-loader.d.ts.map +0 -1
- package/dist/services/channels/provider-loader.js.map +0 -1
- package/dist/services/channels/providers/telegram/adapter.d.ts.map +0 -1
- package/dist/services/channels/providers/telegram/adapter.js.map +0 -1
- package/dist/services/channels/providers/telegram/index.d.ts.map +0 -1
- package/dist/services/channels/providers/telegram/index.js.map +0 -1
- package/dist/services/channels/providers/telegram/normalizer.d.ts.map +0 -1
- package/dist/services/channels/providers/telegram/normalizer.js.map +0 -1
- package/dist/services/channels/providers/telegram/types.d.ts.map +0 -1
- package/dist/services/channels/providers/telegram/types.js.map +0 -1
- package/dist/services/channels/providers/whatsapp/adapter.d.ts.map +0 -1
- package/dist/services/channels/providers/whatsapp/adapter.js.map +0 -1
- package/dist/services/channels/providers/whatsapp/index.d.ts.map +0 -1
- package/dist/services/channels/providers/whatsapp/index.js.map +0 -1
- package/dist/services/channels/providers/whatsapp/normalizer.d.ts.map +0 -1
- package/dist/services/channels/providers/whatsapp/normalizer.js.map +0 -1
- package/dist/services/channels/providers/whatsapp/session.d.ts.map +0 -1
- package/dist/services/channels/providers/whatsapp/session.js.map +0 -1
- package/dist/services/channels/providers/whatsapp/types.d.ts.map +0 -1
- package/dist/services/channels/providers/whatsapp/types.js.map +0 -1
- package/dist/services/channels/reconnect.d.ts.map +0 -1
- package/dist/services/channels/reconnect.js.map +0 -1
- package/dist/services/channels/status-reactions.d.ts.map +0 -1
- package/dist/services/channels/status-reactions.js.map +0 -1
- package/dist/services/channels/types.d.ts.map +0 -1
- package/dist/services/channels/types.js.map +0 -1
- package/dist/services/channels/typing-state.d.ts.map +0 -1
- package/dist/services/channels/typing-state.js.map +0 -1
- package/dist/services/mcp-client/index.d.ts.map +0 -1
- package/dist/services/mcp-client/index.js.map +0 -1
- /package/dist/services/{channels → channel}/base-adapter.js +0 -0
- /package/dist/services/{channels → channel}/debounce.d.ts +0 -0
- /package/dist/services/{channels → channel}/debounce.js +0 -0
- /package/dist/services/{channels → channel}/dedupe.d.ts +0 -0
- /package/dist/services/{channels → channel}/dedupe.js +0 -0
- /package/dist/services/{channels → channel}/delivery.d.ts +0 -0
- /package/dist/services/{channels → channel}/link-expand.d.ts +0 -0
- /package/dist/services/{channels → channel}/link-expand.js +0 -0
- /package/dist/services/{channels → channel}/media-paths.d.ts +0 -0
- /package/dist/services/{channels → channel}/media-paths.js +0 -0
- /package/dist/services/{channels → channel}/pipeline.js +0 -0
- /package/dist/services/{channels → channel}/provider-loader.d.ts +0 -0
- /package/dist/services/{channels → channel}/provider-loader.js +0 -0
- /package/dist/services/{channels → channel}/providers/telegram/adapter.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/telegram/index.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/telegram/index.js +0 -0
- /package/dist/services/{channels → channel}/providers/telegram/normalizer.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/telegram/normalizer.js +0 -0
- /package/dist/services/{channels → channel}/providers/telegram/types.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/telegram/types.js +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/adapter.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/adapter.js +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/index.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/index.js +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/normalizer.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/normalizer.js +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/session.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/session.js +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/types.d.ts +0 -0
- /package/dist/services/{channels → channel}/providers/whatsapp/types.js +0 -0
- /package/dist/services/{channels → channel}/reconnect.d.ts +0 -0
- /package/dist/services/{channels → channel}/reconnect.js +0 -0
- /package/dist/services/{channels → channel}/status-reactions.d.ts +0 -0
- /package/dist/services/{channels → channel}/status-reactions.js +0 -0
- /package/dist/services/{channels → channel}/types.js +0 -0
- /package/dist/services/{channels → channel}/typing-state.d.ts +0 -0
- /package/dist/services/{channels → channel}/typing-state.js +0 -0
|
@@ -7,51 +7,16 @@
|
|
|
7
7
|
* - Debug mode for inspecting tools, prompts, history
|
|
8
8
|
* - Streaming events passthrough to bus (agent.onDelta, agent.onTool, agent.onCompleted)
|
|
9
9
|
*/
|
|
10
|
-
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
11
|
-
var useValue = arguments.length > 2;
|
|
12
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
13
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
14
|
-
}
|
|
15
|
-
return useValue ? value : void 0;
|
|
16
|
-
};
|
|
17
|
-
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
18
|
-
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
19
|
-
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
20
|
-
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
21
|
-
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
22
|
-
var _, done = false;
|
|
23
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
24
|
-
var context = {};
|
|
25
|
-
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
26
|
-
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
27
|
-
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
28
|
-
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
29
|
-
if (kind === "accessor") {
|
|
30
|
-
if (result === void 0) continue;
|
|
31
|
-
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
32
|
-
if (_ = accept(result.get)) descriptor.get = _;
|
|
33
|
-
if (_ = accept(result.set)) descriptor.set = _;
|
|
34
|
-
if (_ = accept(result.init)) initializers.unshift(_);
|
|
35
|
-
}
|
|
36
|
-
else if (_ = accept(result)) {
|
|
37
|
-
if (kind === "field") initializers.unshift(_);
|
|
38
|
-
else descriptor[key] = _;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
42
|
-
done = true;
|
|
43
|
-
};
|
|
44
10
|
import { z } from 'zod';
|
|
45
11
|
import path from 'node:path';
|
|
46
|
-
import { register } from '../../gateway/decorators.js';
|
|
47
12
|
import { createLogger } from '../../lib/logger.js';
|
|
48
13
|
import { parseDirectives } from './directives.js';
|
|
49
|
-
import { withTimeout } from '../../lib/
|
|
14
|
+
import { withTimeout } from '../../lib/util.js';
|
|
50
15
|
import { interpolatePrompt } from './prompt-interpolate.js';
|
|
51
|
-
import { truncate } from '../../lib/
|
|
16
|
+
import { truncate } from '../../lib/util.js';
|
|
52
17
|
import { existsSync, promises as fs } from 'node:fs';
|
|
53
18
|
import { getDataPaths } from '../../lib/paths.js';
|
|
54
|
-
import { parseSessionKey, isSubagentSession } from '../../lib/session-key.js';
|
|
19
|
+
import { parseSessionKey, isSubagentSession, rootSessionKey } from '../../lib/session-key.js';
|
|
55
20
|
// Pi SDK imports
|
|
56
21
|
import { createAgentSession, SessionManager, SettingsManager, AuthStorage, ModelRegistry, DefaultResourceLoader, } from '@earendil-works/pi-coding-agent';
|
|
57
22
|
import { createCustomTools } from './tools.js';
|
|
@@ -62,470 +27,507 @@ const log = createLogger('agent');
|
|
|
62
27
|
// Hardcoded agent execution constants
|
|
63
28
|
const EXECUTION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
64
29
|
// ── AgentService ─────────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
__esDecorate(this, null, _execute_decorators, { kind: "method", name: "execute", static: false, private: false, access: { has: obj => "execute" in obj, get: obj => obj.execute }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
91
|
-
__esDecorate(this, null, _appendMessage_decorators, { kind: "method", name: "appendMessage", static: false, private: false, access: { has: obj => "appendMessage" in obj, get: obj => obj.appendMessage }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
92
|
-
__esDecorate(this, null, _status_decorators, { kind: "method", name: "status", static: false, private: false, access: { has: obj => "status" in obj, get: obj => obj.status }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
93
|
-
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
30
|
+
export class AgentService {
|
|
31
|
+
name = 'agent';
|
|
32
|
+
bus;
|
|
33
|
+
config;
|
|
34
|
+
sessions = new Map();
|
|
35
|
+
/** sessionKey → epoch ms when the session entered the cache (for agent.status). */
|
|
36
|
+
sessionMeta = new Map();
|
|
37
|
+
activeRuns = new Set();
|
|
38
|
+
agentDir;
|
|
39
|
+
authStorage;
|
|
40
|
+
modelRegistry;
|
|
41
|
+
settings;
|
|
42
|
+
async init(bus) {
|
|
43
|
+
this.bus = bus;
|
|
44
|
+
this.config = await bus.call('config.get', {});
|
|
45
|
+
const paths = getDataPaths();
|
|
46
|
+
this.agentDir = path.join(paths.dataDir, 'agent');
|
|
47
|
+
// Use ~/.vargos/agent for auth and models (override PiAgent defaults)
|
|
48
|
+
const authJsonPath = path.join(this.agentDir, 'auth.json');
|
|
49
|
+
const modelsJsonPath = path.join(this.agentDir, 'models.json');
|
|
50
|
+
this.authStorage = AuthStorage.create(authJsonPath);
|
|
51
|
+
this.modelRegistry = ModelRegistry.create(this.authStorage, modelsJsonPath);
|
|
52
|
+
const modelError = this.modelRegistry.getError();
|
|
53
|
+
if (modelError) {
|
|
54
|
+
throw new Error(`Failed to load models from ${modelsJsonPath}: ${modelError}`);
|
|
94
55
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
56
|
+
this.settings = SettingsManager.create(paths.dataDir, this.agentDir);
|
|
57
|
+
this.settings.applyOverrides({
|
|
58
|
+
retry: {
|
|
59
|
+
enabled: true,
|
|
60
|
+
maxRetries: 3,
|
|
61
|
+
baseDelayMs: 1000,
|
|
62
|
+
provider: { timeoutMs: 120000, maxRetries: 3, maxRetryDelayMs: 30000 },
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
this.registerMethods(bus);
|
|
66
|
+
await this.persistRetrySettings();
|
|
67
|
+
}
|
|
68
|
+
registerMethods(bus) {
|
|
69
|
+
bus.register('agent.execute', {
|
|
70
|
+
description: 'Executes a task with the agent, optionally delegating to a subagent.',
|
|
71
|
+
// passthrough keeps `sessionKey` (auto-injected by the tool wrapper, supplied by
|
|
72
|
+
// direct callers) alive through validation without advertising it as a tool param.
|
|
73
|
+
schema: z.object({
|
|
74
|
+
task: z.string().describe('The task to execute.'),
|
|
75
|
+
cwd: z.string().optional().describe('Working directory for the agent — defaults to workspace dir.'),
|
|
76
|
+
model: z.string().optional().describe('Optional model override as "provider:modelId" (e.g. "anthropic:claude-opus-4"). Omit to use the agent default.'),
|
|
77
|
+
}).passthrough(),
|
|
78
|
+
cli: { positional: ['task'] },
|
|
79
|
+
}, (p) => this.execute(p));
|
|
80
|
+
bus.register('agent.appendMessage', {
|
|
81
|
+
description: 'Append a message to a session\'s history without executing the agent.',
|
|
82
|
+
schema: z.object({ sessionKey: z.string(), content: z.string() }),
|
|
83
|
+
internal: true,
|
|
84
|
+
}, (p) => this.appendMessage(p));
|
|
85
|
+
bus.register('agent.status', {
|
|
86
|
+
description: 'Return the agent session inventory (state, parent links, model). Pass sessionKey to scope to one session and its subagents.',
|
|
87
|
+
schema: z.object({ sessionKey: z.string().optional() }),
|
|
88
|
+
cli: { positional: ['sessionKey'] },
|
|
89
|
+
}, (p) => this.status(p));
|
|
90
|
+
}
|
|
91
|
+
dispose() {
|
|
92
|
+
this.sessions.forEach((session) => session.dispose());
|
|
93
|
+
this.sessions.clear();
|
|
94
|
+
this.sessionMeta.clear();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Persist retry settings to disk during boot
|
|
98
|
+
*/
|
|
99
|
+
async persistRetrySettings() {
|
|
100
|
+
try {
|
|
101
|
+
const settingsPath = path.join(this.agentDir, 'settings.json');
|
|
102
|
+
const currentData = await fs.readFile(settingsPath, 'utf-8');
|
|
103
|
+
const currentSettings = JSON.parse(currentData);
|
|
104
|
+
const updated = {
|
|
105
|
+
...currentSettings,
|
|
123
106
|
retry: {
|
|
124
107
|
enabled: true,
|
|
125
108
|
maxRetries: 3,
|
|
126
109
|
baseDelayMs: 1000,
|
|
127
110
|
provider: {
|
|
128
|
-
timeoutMs: 120000,
|
|
111
|
+
timeoutMs: 120000,
|
|
129
112
|
maxRetries: 3,
|
|
130
|
-
maxRetryDelayMs: 30000,
|
|
113
|
+
maxRetryDelayMs: 30000,
|
|
131
114
|
},
|
|
132
115
|
},
|
|
133
|
-
}
|
|
116
|
+
};
|
|
117
|
+
await fs.writeFile(settingsPath, JSON.stringify(updated, null, 2), 'utf-8');
|
|
118
|
+
log.debug('Agent retry settings persisted to settings.json');
|
|
134
119
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*/
|
|
138
|
-
async start() {
|
|
139
|
-
try {
|
|
140
|
-
const settingsPath = path.join(this.agentDir, 'settings.json');
|
|
141
|
-
const currentData = await fs.readFile(settingsPath, 'utf-8');
|
|
142
|
-
const currentSettings = JSON.parse(currentData);
|
|
143
|
-
const updated = {
|
|
144
|
-
...currentSettings,
|
|
145
|
-
retry: {
|
|
146
|
-
enabled: true,
|
|
147
|
-
maxRetries: 3,
|
|
148
|
-
baseDelayMs: 1000,
|
|
149
|
-
provider: {
|
|
150
|
-
timeoutMs: 120000,
|
|
151
|
-
maxRetries: 3,
|
|
152
|
-
maxRetryDelayMs: 30000,
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
await fs.writeFile(settingsPath, JSON.stringify(updated, null, 2), 'utf-8');
|
|
157
|
-
log.debug('Agent retry settings persisted to settings.json');
|
|
158
|
-
}
|
|
159
|
-
catch (err) {
|
|
160
|
-
log.warn(`Failed to persist retry settings: ${err instanceof Error ? err.message : String(err)}`);
|
|
161
|
-
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
log.warn(`Failed to persist retry settings: ${err instanceof Error ? err.message : String(err)}`);
|
|
162
122
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
throw new Error('sessionKey is required for agent.execute');
|
|
173
|
-
}
|
|
174
|
-
log.debug(`execute: START ${params.sessionKey}`);
|
|
175
|
-
// Fall back to the session's default model when the override is missing or unknown,
|
|
176
|
-
// instead of failing the run (agents sometimes pass an ill-formed or stale model id).
|
|
177
|
-
let model = params.model;
|
|
178
|
-
if (model && !this.isValidModel(model)) {
|
|
179
|
-
log.warn(`agent.execute: ignoring invalid model "${model}" (expected provider:modelId) — using default`);
|
|
180
|
-
model = undefined;
|
|
181
|
-
}
|
|
182
|
-
const directives = parseDirectives(params.task);
|
|
183
|
-
const task = interpolatePrompt(directives.cleaned || params.task);
|
|
184
|
-
const session = await this.getOrCreateSession(params.sessionKey, { cwd: params.cwd, model });
|
|
185
|
-
if (directives.thinkingLevel) {
|
|
186
|
-
session.setThinkingLevel(directives.thinkingLevel);
|
|
187
|
-
}
|
|
188
|
-
this.activeRuns.add(params.sessionKey);
|
|
189
|
-
const startTime = Date.now();
|
|
190
|
-
const modelTag = `${session.model?.provider}:${session.model?.id}`;
|
|
191
|
-
try {
|
|
192
|
-
await withTimeout(session.prompt(task, { streamingBehavior: 'steer' }), EXECUTION_TIMEOUT_MS, `Agent execution timeout after ${EXECUTION_TIMEOUT_MS}ms`);
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
this.activeRuns.delete(params.sessionKey);
|
|
196
|
-
}
|
|
197
|
-
const { content, error } = this.extractFinalAssistant(session);
|
|
198
|
-
if (error) {
|
|
199
|
-
log.error(`execute: ${params.sessionKey} ended with error [model=${modelTag}]: ${error}`);
|
|
200
|
-
throw new Error(error);
|
|
201
|
-
}
|
|
202
|
-
const elapsed = Date.now() - startTime;
|
|
203
|
-
log.info(`${params.sessionKey} → ${content.length} chars in ${elapsed}ms (${modelTag})`);
|
|
204
|
-
return { response: content };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* agent.execute — Run a task. `sessionKey` is auto-injected by the tool wrapper when
|
|
126
|
+
* the agent calls this as a tool; direct callers (channels, cron, webhooks, RPC)
|
|
127
|
+
* supply it. It survives validation via the schema's .passthrough().
|
|
128
|
+
*/
|
|
129
|
+
async execute(params) {
|
|
130
|
+
if (!params.sessionKey) {
|
|
131
|
+
throw new Error('sessionKey is required for agent.execute');
|
|
205
132
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
133
|
+
log.debug(`execute: START ${params.sessionKey}`);
|
|
134
|
+
// Fall back to the session's default model when the override is missing or unknown,
|
|
135
|
+
// instead of failing the run (agents sometimes pass an ill-formed or stale model id).
|
|
136
|
+
let model = params.model;
|
|
137
|
+
if (model && !this.isValidModel(model)) {
|
|
138
|
+
log.warn(`agent.execute: ignoring invalid model "${model}" (expected provider:modelId) — using default`);
|
|
139
|
+
model = undefined;
|
|
140
|
+
}
|
|
141
|
+
const directives = parseDirectives(params.task);
|
|
142
|
+
const task = interpolatePrompt(directives.cleaned || params.task);
|
|
143
|
+
const session = await this.getOrCreateSession(params.sessionKey, { cwd: params.cwd, model });
|
|
144
|
+
if (directives.thinkingLevel) {
|
|
145
|
+
session.setThinkingLevel(directives.thinkingLevel);
|
|
146
|
+
}
|
|
147
|
+
this.activeRuns.add(params.sessionKey);
|
|
148
|
+
const startTime = Date.now();
|
|
149
|
+
const modelTag = `${session.model?.provider}:${session.model?.id}`;
|
|
150
|
+
try {
|
|
151
|
+
await withTimeout(session.prompt(task, { streamingBehavior: 'steer' }), EXECUTION_TIMEOUT_MS, `Agent execution timeout after ${EXECUTION_TIMEOUT_MS}ms`);
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
this.activeRuns.delete(params.sessionKey);
|
|
155
|
+
}
|
|
156
|
+
const { content, error } = this.extractFinalAssistant(session);
|
|
157
|
+
if (error) {
|
|
158
|
+
log.error(`execute: ${params.sessionKey} ended with error [model=${modelTag}]: ${error}`);
|
|
159
|
+
throw new Error(error);
|
|
160
|
+
}
|
|
161
|
+
const elapsed = Date.now() - startTime;
|
|
162
|
+
log.info(`${params.sessionKey} → ${content.length} chars in ${elapsed}ms (${modelTag})`);
|
|
163
|
+
return { response: content };
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* agent.appendMessage — Append message to session JSONL without executing agent.
|
|
167
|
+
* Records inbound messages in session history (observe-only for non-whitelisted).
|
|
168
|
+
* Internal only — not exposed as an agent tool.
|
|
169
|
+
*/
|
|
170
|
+
async appendMessage(params) {
|
|
171
|
+
const session = await this.getOrCreateSession(params.sessionKey);
|
|
172
|
+
const sessionFile = session.sessionManager.getSessionFile();
|
|
173
|
+
if (!sessionFile) {
|
|
174
|
+
log.debug(`No session file for ${params.sessionKey}, skipping append`);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
log.debug(`Appending message to session ${params.sessionKey} (no execution)`);
|
|
178
|
+
const isExecuting = this.activeRuns.has(params.sessionKey);
|
|
179
|
+
session.sessionManager.appendMessage({
|
|
180
|
+
timestamp: Date.now(),
|
|
181
|
+
role: 'user',
|
|
182
|
+
content: params.content,
|
|
183
|
+
});
|
|
184
|
+
if (!isExecuting) {
|
|
185
|
+
// Manually force write to disk so other Vargos instances on the NAS/cluster can see the history
|
|
224
186
|
session.exportToJsonl(sessionFile);
|
|
187
|
+
// We MUST wipe the session from the local Vargos cache if the agent is not executing.
|
|
188
|
+
// Why? The Pi SDK deliberately defers JSONL file creation until the FIRST assistant message.
|
|
189
|
+
// By forcing `exportToJsonl`, we circumvented Pi SDK and created the file early on disk.
|
|
190
|
+
// But this in-memory AgentSession's `flushed` flag remains `false`.
|
|
191
|
+
// If kept in cache, the NEXT time this node executes the LLM, the Pi SDK will attempt
|
|
192
|
+
// an exclusive create (`openSync(..., "wx")`) and violently crash with `EEXIST` because
|
|
193
|
+
// the file is already there! Evicting cache forces full reload via `continueRecent()`.
|
|
194
|
+
session.dispose();
|
|
195
|
+
this.sessions.delete(params.sessionKey);
|
|
196
|
+
this.sessionMeta.delete(params.sessionKey);
|
|
225
197
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* agent.status — Inventory cached sessions (parents, subagents, idle) with their
|
|
201
|
+
* run state, parent relationship, and model. When `sessionKey` is given, the result
|
|
202
|
+
* is scoped to that session and its subagents — letting a parent observe its own
|
|
203
|
+
* subtree. `activeRuns` is kept for callers that only need the executing keys.
|
|
204
|
+
*/
|
|
205
|
+
async status(params) {
|
|
206
|
+
const scope = params.sessionKey;
|
|
207
|
+
const inScope = (key) => !scope || key === scope || key.startsWith(`${scope}:subagent:`);
|
|
208
|
+
const sessions = Array.from(this.sessions.entries())
|
|
209
|
+
.filter(([key]) => inScope(key))
|
|
210
|
+
.map(([sessionKey, session]) => ({
|
|
211
|
+
sessionKey,
|
|
212
|
+
state: this.activeRuns.has(sessionKey) ? 'running' : 'idle',
|
|
213
|
+
parentKey: isSubagentSession(sessionKey) ? rootSessionKey(sessionKey) : undefined,
|
|
214
|
+
model: session.model ? `${session.model.provider}:${session.model.id}` : undefined,
|
|
215
|
+
startedAt: this.sessionMeta.get(sessionKey),
|
|
216
|
+
}));
|
|
217
|
+
const activeRuns = Array.from(this.activeRuns).filter(inScope);
|
|
218
|
+
return { sessions, activeRuns };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get or create AgentSession for sessionKey.
|
|
222
|
+
* Uses SessionManager.continueRecent() to load the latest session file,
|
|
223
|
+
* preserving conversation history across restarts.
|
|
224
|
+
*/
|
|
225
|
+
async getOrCreateSession(sessionKey, options) {
|
|
226
|
+
const cached = this.sessions.get(sessionKey);
|
|
227
|
+
if (cached) {
|
|
228
|
+
await this.applyModelOverride(cached, sessionKey, options?.model);
|
|
229
|
+
return cached;
|
|
231
230
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
let sessionManager;
|
|
248
|
-
try {
|
|
249
|
-
sessionManager = SessionManager.create(effectiveCwd, sessionDir);
|
|
231
|
+
const paths = getDataPaths();
|
|
232
|
+
const effectiveCwd = options?.cwd ?? paths.dataDir;
|
|
233
|
+
const sessionDir = path.join(paths.sessionsDir, sessionKey.replace(/:/g, path.sep));
|
|
234
|
+
// Use continueRecent to find and load the latest session file (preserves history).
|
|
235
|
+
// Falls back to create() if no existing session file is found.
|
|
236
|
+
let sessionManager;
|
|
237
|
+
try {
|
|
238
|
+
sessionManager = SessionManager.create(effectiveCwd, sessionDir);
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
if (err instanceof Error && 'code' in err && err.code === 'EEXIST') {
|
|
242
|
+
// File was created by another code path (e.g. concurrent message for same session).
|
|
243
|
+
// Fall back to continueRecent which opens existing files gracefully.
|
|
244
|
+
log.debug(`session ${sessionKey}: create() hit EEXIST, falling back to continueRecent()`);
|
|
245
|
+
sessionManager = SessionManager.continueRecent(effectiveCwd, sessionDir);
|
|
250
246
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
// File was created by another code path (e.g. concurrent message for same session).
|
|
254
|
-
// Fall back to continueRecent which opens existing files gracefully.
|
|
255
|
-
log.debug(`session ${sessionKey}: create() hit EEXIST, falling back to continueRecent()`);
|
|
256
|
-
sessionManager = SessionManager.continueRecent(effectiveCwd, sessionDir);
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
throw err;
|
|
260
|
-
}
|
|
247
|
+
else {
|
|
248
|
+
throw err;
|
|
261
249
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
250
|
+
}
|
|
251
|
+
await fs.mkdir(sessionDir, { recursive: true });
|
|
252
|
+
await fs.mkdir(this.agentDir, { recursive: true });
|
|
253
|
+
const persona = await this.loadPersonaIfChannel(sessionKey);
|
|
254
|
+
const customTools = await this.getCustomTools(sessionKey, persona?.meta.allowedTools);
|
|
255
|
+
const rawSystemPrompt = await this.getSystemPrompt(sessionKey, persona?.body);
|
|
256
|
+
const resourceLoader = await this.createResourceLoader(rawSystemPrompt, effectiveCwd);
|
|
257
|
+
log.debug(`session: ${sessionKey} created (${customTools.length} tools, ${rawSystemPrompt?.length ?? 0} chars prompt)`);
|
|
258
|
+
// Apply the per-call/channel model override at creation time, when provided and known.
|
|
259
|
+
const model = this.resolveModel(options?.model);
|
|
260
|
+
const { session } = await this.createPiSession({
|
|
261
|
+
cwd: effectiveCwd,
|
|
262
|
+
agentDir: this.agentDir,
|
|
263
|
+
sessionManager,
|
|
264
|
+
settingsManager: this.settings,
|
|
265
|
+
authStorage: this.authStorage,
|
|
266
|
+
modelRegistry: this.modelRegistry,
|
|
267
|
+
customTools,
|
|
268
|
+
resourceLoader,
|
|
269
|
+
...(model && { model }),
|
|
270
|
+
});
|
|
271
|
+
if (process.env.LOG_LEVEL === 'debug') {
|
|
272
|
+
const debugDir = path.join(sessionDir, '.debug');
|
|
273
|
+
if (!existsSync(debugDir)) {
|
|
274
|
+
await fs.mkdir(debugDir, { recursive: true });
|
|
286
275
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return session;
|
|
276
|
+
log.debug(`Storing debug files in session's debug directory: ${debugDir}`);
|
|
277
|
+
await fs.writeFile(path.join(debugDir, `systemPrompt.md`), session.systemPrompt ?? '', 'utf-8');
|
|
290
278
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
279
|
+
this.subscribeToSessionEvents(session, sessionKey);
|
|
280
|
+
this.sessions.set(sessionKey, session);
|
|
281
|
+
this.sessionMeta.set(sessionKey, Date.now());
|
|
282
|
+
return session;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Create the underlying Pi SDK session. Isolated as a seam so tests can
|
|
286
|
+
* substitute a fake session without the SDK's model/auth machinery.
|
|
287
|
+
*/
|
|
288
|
+
createPiSession(options) {
|
|
289
|
+
return createAgentSession(options);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Switch a cached session's model when an override differs from the current one.
|
|
293
|
+
* Unknown/missing overrides are a no-op — the session keeps its existing model.
|
|
294
|
+
*/
|
|
295
|
+
async applyModelOverride(session, sessionKey, modelSpec) {
|
|
296
|
+
const resolved = this.resolveModel(modelSpec);
|
|
297
|
+
if (!resolved)
|
|
298
|
+
return;
|
|
299
|
+
if (session.model?.provider === resolved.provider && session.model?.id === resolved.id)
|
|
300
|
+
return;
|
|
301
|
+
await session.setModel(resolved);
|
|
302
|
+
log.info(`session ${sessionKey}: model → ${resolved.provider}:${resolved.id}`);
|
|
303
|
+
}
|
|
304
|
+
/** Resolve a `provider:modelId` override to a Pi SDK model, or undefined if unknown. */
|
|
305
|
+
resolveModel(modelSpec) {
|
|
306
|
+
if (!modelSpec)
|
|
307
|
+
return undefined;
|
|
308
|
+
const [provider, modelId] = modelSpec.split(':');
|
|
309
|
+
return this.modelRegistry.find(provider, modelId);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Subscribe to PiAgent sessionsubscription - emit to bus for streaming + debug logging.
|
|
313
|
+
*/
|
|
314
|
+
subscribeToSessionEvents(session, sessionKey) {
|
|
315
|
+
session.subscribe((event) => {
|
|
316
|
+
const eventType = event.type;
|
|
317
|
+
// log.debug(` --- :: Agent Lifecycle = ${eventType} --- :: ${sessionKey} --- :: `);
|
|
318
|
+
// Skip session-specific events (auto_retry_start, auto_retry_end) - not emitted as bus events
|
|
319
|
+
if (eventType === 'auto_retry_start' || eventType === 'auto_retry_end') {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
// Map PiAgent events to our bus events
|
|
323
|
+
// Bridge PiAgent's untyped event structure to our typed EventMap
|
|
324
|
+
switch (eventType) {
|
|
325
|
+
case 'tool_execution_start': {
|
|
326
|
+
const e = event;
|
|
327
|
+
if (e.toolName) {
|
|
328
|
+
this.bus.emit('agent.onTool', {
|
|
329
|
+
sessionKey,
|
|
330
|
+
toolName: e.toolName,
|
|
331
|
+
phase: 'start',
|
|
332
|
+
args: (e.args ?? {}),
|
|
333
|
+
});
|
|
328
334
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
case 'tool_execution_end': {
|
|
338
|
+
const e = event;
|
|
339
|
+
if (e.toolName) {
|
|
340
|
+
this.bus.emit('agent.onTool', {
|
|
341
|
+
sessionKey,
|
|
342
|
+
toolName: e.toolName,
|
|
343
|
+
phase: 'end',
|
|
344
|
+
result: (e.result ?? {}),
|
|
345
|
+
});
|
|
336
346
|
}
|
|
337
|
-
|
|
338
|
-
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
case 'message_update': {
|
|
350
|
+
const e = event;
|
|
351
|
+
const delta = e.delta || e.text || '';
|
|
352
|
+
if (delta) {
|
|
353
|
+
this.bus.emit('agent.onDelta', { sessionKey, chunk: delta });
|
|
339
354
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
break;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
case 'turn_end': {
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
360
|
+
case 'agent_end': {
|
|
361
|
+
const { content, error } = this.extractFinalAssistant(session);
|
|
362
|
+
if (error) {
|
|
363
|
+
const model = session.model?.id ?? 'unknown';
|
|
364
|
+
log.error(`agent_end with error for ${sessionKey} [model=${model}]: ${error}`);
|
|
365
|
+
this.bus.emit('agent.onCompleted', { sessionKey, success: false, error });
|
|
353
366
|
}
|
|
354
|
-
|
|
355
|
-
|
|
367
|
+
else {
|
|
368
|
+
log.debug(` emitting agent.onCompleted with ${content.length} chars`);
|
|
369
|
+
this.bus.emit('agent.onCompleted', { sessionKey, success: true, response: content });
|
|
356
370
|
}
|
|
371
|
+
break;
|
|
357
372
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Create ResourceLoader. PiAgent's DefaultResourceLoader handles skills, themes, and
|
|
362
|
-
* prompt templates. We override systemPrompt with our Vargos bootstrap files.
|
|
363
|
-
*/
|
|
364
|
-
async createResourceLoader(systemPromptOverride, cwd) {
|
|
365
|
-
const paths = getDataPaths();
|
|
366
|
-
const effectiveCwd = cwd ?? paths.workspaceDir;
|
|
367
|
-
// Only workspace + cwd here — Pi SDK already auto-loads <agentDir>/skills and <cwd>/.pi/skills.
|
|
368
|
-
const skillPaths = resolveSkillPaths(paths.workspaceDir, ...(cwd ? [cwd] : []));
|
|
369
|
-
const resourceLoader = new DefaultResourceLoader({
|
|
370
|
-
cwd: effectiveCwd,
|
|
371
|
-
agentDir: this.agentDir,
|
|
372
|
-
settingsManager: this.settings,
|
|
373
|
-
extensionFactories: [],
|
|
374
|
-
additionalSkillPaths: skillPaths,
|
|
375
|
-
noSkills: false,
|
|
376
|
-
...(systemPromptOverride && { systemPrompt: systemPromptOverride }),
|
|
377
|
-
});
|
|
378
|
-
await resourceLoader.reload();
|
|
379
|
-
const { skills } = resourceLoader.getSkills();
|
|
380
|
-
log.debug(`Resource loader loaded with ${skills.length} skills.`);
|
|
381
|
-
return resourceLoader;
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Load persona for the given sessionKey.
|
|
385
|
-
* - Subagent sessions: load `agents/subagent.md` (preamble + allowedTools whitelist).
|
|
386
|
-
* - Channel sessions: load `agents/<channelId>.md` (persona + tool filter).
|
|
387
|
-
* - Cron / CLI / other types: return null (no persona override applied).
|
|
388
|
-
*/
|
|
389
|
-
async loadPersonaIfChannel(sessionKey) {
|
|
390
|
-
if (isSubagentSession(sessionKey))
|
|
391
|
-
return loadSubagentPersona();
|
|
392
|
-
const { type } = parseSessionKey(sessionKey);
|
|
393
|
-
const isChannel = this.config.channels.some(c => c.id === type);
|
|
394
|
-
if (!isChannel)
|
|
395
|
-
return null;
|
|
396
|
-
return loadChannelPersona(type);
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Build system prompt.
|
|
400
|
-
* - Subagent sessions: return the persona body from `agents/subagent.md`.
|
|
401
|
-
* No bootstrap files (AGENTS.md, SOUL.md, TOOLS.md) are loaded — the parent's
|
|
402
|
-
* task description is the subagent's sole context.
|
|
403
|
-
* - Parent/other sessions: merge AGENTS.md + SOUL.md + TOOLS.md from workspace/cwd,
|
|
404
|
-
* then append channel persona body if provided.
|
|
405
|
-
*/
|
|
406
|
-
async getSystemPrompt(sessionKey, personaBody) {
|
|
407
|
-
if (isSubagentSession(sessionKey)) {
|
|
408
|
-
return personaBody?.trim() || undefined;
|
|
409
|
-
}
|
|
410
|
-
const bootstrapFiles = ['AGENTS.md', 'SOUL.md', 'TOOLS.md'];
|
|
411
|
-
const maxCharsPerFile = 6000;
|
|
412
|
-
const dirs = [getDataPaths().workspaceDir];
|
|
413
|
-
const filePathsToLoad = [];
|
|
414
|
-
for (const dir of dirs) {
|
|
415
|
-
for (const filename of bootstrapFiles) {
|
|
416
|
-
filePathsToLoad.push({ dir, filename, path: path.join(dir, filename) });
|
|
373
|
+
default: {
|
|
374
|
+
break;
|
|
417
375
|
}
|
|
418
376
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Create ResourceLoader. PiAgent's DefaultResourceLoader handles skills, themes, and
|
|
381
|
+
* prompt templates. We override systemPrompt with our Vargos bootstrap files.
|
|
382
|
+
*/
|
|
383
|
+
async createResourceLoader(systemPromptOverride, cwd) {
|
|
384
|
+
const paths = getDataPaths();
|
|
385
|
+
const effectiveCwd = cwd ?? paths.workspaceDir;
|
|
386
|
+
// Only workspace + cwd here — Pi SDK already auto-loads <agentDir>/skills and <cwd>/.pi/skills.
|
|
387
|
+
const skillPaths = resolveSkillPaths(paths.workspaceDir, ...(cwd ? [cwd] : []));
|
|
388
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
389
|
+
cwd: effectiveCwd,
|
|
390
|
+
agentDir: this.agentDir,
|
|
391
|
+
settingsManager: this.settings,
|
|
392
|
+
extensionFactories: [],
|
|
393
|
+
additionalSkillPaths: skillPaths,
|
|
394
|
+
noSkills: false,
|
|
395
|
+
...(systemPromptOverride && { systemPrompt: systemPromptOverride }),
|
|
396
|
+
});
|
|
397
|
+
await resourceLoader.reload();
|
|
398
|
+
const { skills } = resourceLoader.getSkills();
|
|
399
|
+
log.debug(`Resource loader loaded with ${skills.length} skills.`);
|
|
400
|
+
return resourceLoader;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Load persona for the given sessionKey.
|
|
404
|
+
* - Subagent sessions: load `agents/subagent.md` (preamble + allowedTools whitelist).
|
|
405
|
+
* - Channel sessions: load `agents/<channelId>.md` (persona + tool filter).
|
|
406
|
+
* - Cron / CLI / other types: return null (no persona override applied).
|
|
407
|
+
*/
|
|
408
|
+
async loadPersonaIfChannel(sessionKey) {
|
|
409
|
+
if (isSubagentSession(sessionKey))
|
|
410
|
+
return loadSubagentPersona();
|
|
411
|
+
const { type } = parseSessionKey(sessionKey);
|
|
412
|
+
const isChannel = this.config.channels.some(c => c.id === type);
|
|
413
|
+
if (!isChannel)
|
|
414
|
+
return null;
|
|
415
|
+
return loadChannelPersona(type);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Build system prompt.
|
|
419
|
+
* - Subagent sessions: return the persona body from `agents/subagent.md`.
|
|
420
|
+
* No bootstrap files (AGENTS.md, SOUL.md, TOOLS.md) are loaded — the parent's
|
|
421
|
+
* task description is the subagent's sole context.
|
|
422
|
+
* - Parent/other sessions: merge AGENTS.md + SOUL.md + TOOLS.md from workspace/cwd,
|
|
423
|
+
* then append channel persona body if provided.
|
|
424
|
+
*/
|
|
425
|
+
async getSystemPrompt(sessionKey, personaBody) {
|
|
426
|
+
if (isSubagentSession(sessionKey)) {
|
|
427
|
+
return personaBody?.trim() || undefined;
|
|
428
|
+
}
|
|
429
|
+
const bootstrapFiles = ['AGENTS.md', 'SOUL.md', 'TOOLS.md'];
|
|
430
|
+
const maxCharsPerFile = 6000;
|
|
431
|
+
const dirs = [getDataPaths().workspaceDir];
|
|
432
|
+
const filePathsToLoad = [];
|
|
433
|
+
for (const dir of dirs) {
|
|
434
|
+
for (const filename of bootstrapFiles) {
|
|
435
|
+
filePathsToLoad.push({ dir, filename, path: path.join(dir, filename) });
|
|
438
436
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
437
|
+
}
|
|
438
|
+
const fileContents = await Promise.all(filePathsToLoad.map(async (item) => {
|
|
439
|
+
try {
|
|
440
|
+
const content = await fs.readFile(item.path, 'utf-8');
|
|
441
|
+
const truncated = truncate(content, maxCharsPerFile);
|
|
442
|
+
log.debug(`Loaded ${item.dir}/${item.filename}: ${truncated.length} chars`);
|
|
443
|
+
return {
|
|
444
|
+
label: `<!-- ${item.dir}/${item.filename} -->`,
|
|
445
|
+
content: truncated.trim(),
|
|
446
|
+
};
|
|
443
447
|
}
|
|
444
|
-
|
|
445
|
-
log.debug(
|
|
446
|
-
return
|
|
448
|
+
catch {
|
|
449
|
+
log.debug(`${item.dir}/${item.filename}: not found`);
|
|
450
|
+
return null;
|
|
447
451
|
}
|
|
448
|
-
|
|
449
|
-
|
|
452
|
+
}));
|
|
453
|
+
const sections = [];
|
|
454
|
+
for (const result of fileContents) {
|
|
455
|
+
if (result)
|
|
456
|
+
sections.push(result.label, result.content, '');
|
|
450
457
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
*/
|
|
456
|
-
async getCustomTools(sessionKey, allowedPatterns) {
|
|
457
|
-
const tools = await createCustomTools(sessionKey, this.bus);
|
|
458
|
-
if (!allowedPatterns?.length)
|
|
459
|
-
return tools;
|
|
460
|
-
// Match on `label` (original event name with dots, e.g. "memory.search")
|
|
461
|
-
// rather than `name` (sanitized with dashes, e.g. "memory-search"),
|
|
462
|
-
// so that frontmatter patterns like "memory.*" work as expected.
|
|
463
|
-
return tools.filter(t => allowedPatterns.some(p => matchesGlob(p, t.label)));
|
|
458
|
+
// Also log bootstrap files loaded
|
|
459
|
+
log.debug(`session: ${sessionKey} bootstrap ${sections.filter(s => s.startsWith('<!--')).length} files, ${sections.join('\n').length} chars`);
|
|
460
|
+
if (personaBody) {
|
|
461
|
+
sections.push('<!-- channel persona -->', '<channel-persona>', personaBody.trim(), '</channel-persona>');
|
|
464
462
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
isValidModel(modelSpec) {
|
|
469
|
-
const [provider, modelId] = modelSpec.split(':');
|
|
470
|
-
return !!this.modelRegistry.find(provider, modelId);
|
|
463
|
+
if (sections.length === 0) {
|
|
464
|
+
log.debug('No bootstrap files found, using PiAgent default');
|
|
465
|
+
return undefined;
|
|
471
466
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
467
|
+
const prompt = sections.join('\n');
|
|
468
|
+
return interpolatePrompt(prompt, { SESSION_KEY: sessionKey });
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Load custom tools from bus callable events. When `allowedPatterns` is provided
|
|
472
|
+
* (from a channel persona), filter the tool list down to names matching at least
|
|
473
|
+
* one glob pattern. Empty/undefined patterns = all tools allowed.
|
|
474
|
+
*/
|
|
475
|
+
async getCustomTools(sessionKey, allowedPatterns) {
|
|
476
|
+
const tools = createCustomTools(sessionKey, this.bus);
|
|
477
|
+
if (!allowedPatterns?.length)
|
|
478
|
+
return tools;
|
|
479
|
+
// Match on `label` (original event name with dots, e.g. "memory.search")
|
|
480
|
+
// rather than `name` (sanitized with dashes, e.g. "memory-search"),
|
|
481
|
+
// so that frontmatter patterns like "memory.*" work as expected.
|
|
482
|
+
return tools.filter(t => allowedPatterns.some(p => matchesGlob(p, t.label)));
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Validate model override if provided.
|
|
486
|
+
*/
|
|
487
|
+
isValidModel(modelSpec) {
|
|
488
|
+
return !!this.resolveModel(modelSpec);
|
|
489
|
+
}
|
|
490
|
+
// ── Private Helpers ────────────────────────────────────────────────────────
|
|
491
|
+
/**
|
|
492
|
+
* Extract the final assistant message: text content + error (when stopReason === 'error').
|
|
493
|
+
* Pi SDK records inference failures (e.g. missing API key) as assistant messages with
|
|
494
|
+
* empty content and `errorMessage` populated, instead of throwing — without inspecting
|
|
495
|
+
* `stopReason`/`errorMessage` here, those would surface as silent empty completions.
|
|
496
|
+
*/
|
|
497
|
+
extractFinalAssistant(session) {
|
|
498
|
+
const messages = session.state.messages;
|
|
499
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
500
|
+
const msg = messages[i];
|
|
501
|
+
if (msg?.role !== 'assistant')
|
|
502
|
+
continue;
|
|
503
|
+
let error;
|
|
504
|
+
if (msg.stopReason === 'error') {
|
|
505
|
+
error = msg.errorMessage ?? 'unknown inference error';
|
|
506
|
+
// Log full message details for debugging connection/auth issues
|
|
507
|
+
log.debug('agent error details:', {
|
|
508
|
+
stopReason: msg.stopReason,
|
|
509
|
+
errorMessage: msg.errorMessage,
|
|
510
|
+
model: session.model?.id,
|
|
511
|
+
messageCount: messages.length,
|
|
512
|
+
});
|
|
511
513
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
514
|
+
let content = '';
|
|
515
|
+
if (typeof msg.content === 'string') {
|
|
516
|
+
content = msg.content;
|
|
517
|
+
}
|
|
518
|
+
else if (Array.isArray(msg.content)) {
|
|
519
|
+
content = msg.content
|
|
520
|
+
.filter(block => block.type === 'text')
|
|
521
|
+
.map(block => block.text || '')
|
|
522
|
+
.filter(Boolean)
|
|
523
|
+
.join('\n');
|
|
524
|
+
}
|
|
525
|
+
return error ? { content, error } : { content };
|
|
519
526
|
}
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
const config = await bus.call('config.get', {});
|
|
526
|
-
const runtime = new AgentService({ bus, config });
|
|
527
|
-
bus.bootstrap(runtime);
|
|
528
|
-
await runtime.start(); // Persist retry settings
|
|
529
|
-
return { stop: () => runtime.stop() };
|
|
527
|
+
return { content: '' };
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
export function createService() {
|
|
531
|
+
return new AgentService();
|
|
530
532
|
}
|
|
531
533
|
//# sourceMappingURL=index.js.map
|