@geminixiang/mikan 0.3.2 → 0.4.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/CHANGELOG.md +36 -0
- package/dist/adapter.d.ts +1 -138
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +1 -4
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +25 -33
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +28 -0
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/discord/types.d.ts +6 -0
- package/dist/adapters/discord/types.d.ts.map +1 -0
- package/dist/adapters/discord/types.js +2 -0
- package/dist/adapters/discord/types.js.map +1 -0
- package/dist/adapters/intake.d.ts +11 -0
- package/dist/adapters/intake.d.ts.map +1 -0
- package/dist/adapters/intake.js +42 -0
- package/dist/adapters/intake.js.map +1 -0
- package/dist/adapters/shared.d.ts +7 -31
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/shared.js +18 -2
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +14 -33
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +148 -116
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts +3 -4
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +97 -14
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +5 -20
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/slack/types.d.ts +84 -0
- package/dist/adapters/slack/types.d.ts.map +1 -0
- package/dist/adapters/slack/types.js +2 -0
- package/dist/adapters/slack/types.js.map +1 -0
- package/dist/adapters/streaming.d.ts +18 -0
- package/dist/adapters/streaming.d.ts.map +1 -0
- package/dist/adapters/streaming.js +44 -0
- package/dist/adapters/streaming.js.map +1 -0
- package/dist/adapters/telegram/bot.d.ts +1 -4
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +32 -39
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +33 -0
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/adapters/telegram/types.d.ts +6 -0
- package/dist/adapters/telegram/types.d.ts.map +1 -0
- package/dist/adapters/telegram/types.js +2 -0
- package/dist/adapters/telegram/types.js.map +1 -0
- package/dist/adapters/types.d.ts +58 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/agent.d.ts +4 -16
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +33 -20
- package/dist/agent.js.map +1 -1
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +1 -1
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/auto-reply.d.ts.map +1 -1
- package/dist/commands/auto-reply.js +1 -8
- package/dist/commands/auto-reply.js.map +1 -1
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +3 -3
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/model.d.ts +5 -8
- package/dist/commands/model.d.ts.map +1 -1
- package/dist/commands/model.js +15 -20
- package/dist/commands/model.js.map +1 -1
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +5 -10
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/parse.d.ts.map +1 -1
- package/dist/commands/parse.js +1 -4
- package/dist/commands/parse.js.map +1 -1
- package/dist/commands/registry.d.ts +1 -0
- package/dist/commands/registry.d.ts.map +1 -1
- package/dist/commands/registry.js +23 -0
- package/dist/commands/registry.js.map +1 -1
- package/dist/commands/sandbox.d.ts +2 -5
- package/dist/commands/sandbox.d.ts.map +1 -1
- package/dist/commands/sandbox.js +11 -16
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/commands/session-view.d.ts.map +1 -1
- package/dist/commands/session-view.js +10 -15
- package/dist/commands/session-view.js.map +1 -1
- package/dist/commands/types.d.ts +11 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/config.d.ts +6 -28
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -41
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +1 -15
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js.map +1 -1
- package/dist/events.d.ts +4 -44
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +24 -43
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts +3 -7
- package/dist/execution-resolver.d.ts.map +1 -1
- package/dist/execution-resolver.js +8 -8
- package/dist/execution-resolver.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +2 -6
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +1 -37
- package/dist/log.js.map +1 -1
- package/dist/main.d.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +16 -16
- package/dist/main.js.map +1 -1
- package/dist/observability/instrument.d.ts.map +1 -0
- package/dist/{instrument.js → observability/instrument.js} +2 -2
- package/dist/observability/instrument.js.map +1 -0
- package/dist/{sentry.d.ts → observability/sentry.d.ts} +10 -30
- package/dist/observability/sentry.d.ts.map +1 -0
- package/dist/{sentry.js → observability/sentry.js} +70 -6
- package/dist/observability/sentry.js.map +1 -0
- package/dist/observability/types.d.ts +47 -0
- package/dist/observability/types.d.ts.map +1 -0
- package/dist/observability/types.js +2 -0
- package/dist/observability/types.js.map +1 -0
- package/dist/{ui-copy.d.ts → platform-messages.d.ts} +1 -1
- package/dist/platform-messages.d.ts.map +1 -0
- package/dist/{ui-copy.js → platform-messages.js} +1 -1
- package/dist/platform-messages.js.map +1 -0
- package/dist/portal-shell.d.ts +2 -28
- package/dist/portal-shell.d.ts.map +1 -1
- package/dist/portal-shell.js +2 -2
- package/dist/portal-shell.js.map +1 -1
- package/dist/provisioner.d.ts +2 -23
- package/dist/provisioner.d.ts.map +1 -1
- package/dist/provisioner.js +1 -1
- package/dist/provisioner.js.map +1 -1
- package/dist/runtime/conversation-orchestrator.d.ts +4 -19
- package/dist/runtime/conversation-orchestrator.d.ts.map +1 -1
- package/dist/runtime/conversation-orchestrator.js +17 -8
- package/dist/runtime/conversation-orchestrator.js.map +1 -1
- package/dist/runtime/session-runtime.d.ts +2 -23
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/session-runtime.js +7 -9
- package/dist/runtime/session-runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +35 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime/types.js +2 -0
- package/dist/runtime/types.js.map +1 -0
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +1 -1
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +1 -4
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sessions/chat-session-manager.d.ts +2 -46
- package/dist/sessions/chat-session-manager.d.ts.map +1 -1
- package/dist/sessions/chat-session-manager.js +12 -40
- package/dist/sessions/chat-session-manager.js.map +1 -1
- package/dist/sessions/metadata.d.ts +1 -13
- package/dist/sessions/metadata.d.ts.map +1 -1
- package/dist/sessions/metadata.js.map +1 -1
- package/dist/sessions/policy.d.ts +3 -10
- package/dist/sessions/policy.d.ts.map +1 -1
- package/dist/sessions/policy.js.map +1 -1
- package/dist/sessions/store.d.ts +1 -12
- package/dist/sessions/store.d.ts.map +1 -1
- package/dist/sessions/store.js +4 -7
- package/dist/sessions/store.js.map +1 -1
- package/dist/sessions/types.d.ts +76 -0
- package/dist/sessions/types.d.ts.map +1 -0
- package/dist/sessions/types.js +2 -0
- package/dist/sessions/types.js.map +1 -0
- package/dist/store.d.ts +2 -19
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +1 -1
- package/dist/store.js.map +1 -1
- package/dist/tools/event.d.ts +30 -36
- package/dist/tools/event.d.ts.map +1 -1
- package/dist/tools/event.js +207 -26
- package/dist/tools/event.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/sandbox.d.ts.map +1 -1
- package/dist/tools/sandbox.js +1 -1
- package/dist/tools/sandbox.js.map +1 -1
- package/dist/tools/truncate.d.ts +2 -26
- package/dist/tools/truncate.d.ts.map +1 -1
- package/dist/tools/truncate.js.map +1 -1
- package/dist/tools/types.d.ts +54 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/trigger.d.ts +2 -13
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js.map +1 -1
- package/dist/types.d.ts +307 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/date.d.ts +10 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +23 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/file-guards.d.ts.map +1 -0
- package/dist/utils/file-guards.js.map +1 -0
- package/dist/utils/fs-atomic.d.ts.map +1 -0
- package/dist/utils/fs-atomic.js.map +1 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/http-body.d.ts +10 -0
- package/dist/utils/http-body.d.ts.map +1 -0
- package/dist/utils/http-body.js +34 -0
- package/dist/utils/http-body.js.map +1 -0
- package/dist/vault/index.d.ts +34 -0
- package/dist/vault/index.d.ts.map +1 -0
- package/dist/{vault.js → vault/index.js} +4 -4
- package/dist/vault/index.js.map +1 -0
- package/dist/{vault-routing.d.ts → vault/routing.d.ts} +2 -2
- package/dist/vault/routing.d.ts.map +1 -0
- package/dist/{vault-routing.js → vault/routing.js} +2 -2
- package/dist/vault/routing.js.map +1 -0
- package/dist/{vault.d.ts → vault/types.d.ts} +3 -34
- package/dist/vault/types.d.ts.map +1 -0
- package/dist/vault/types.js +2 -0
- package/dist/vault/types.js.map +1 -0
- package/dist/web/admin/portal.d.ts +5 -0
- package/dist/web/admin/portal.d.ts.map +1 -0
- package/dist/{admin → web/admin}/portal.js +140 -52
- package/dist/web/admin/portal.js.map +1 -0
- package/dist/web/admin/store.d.ts +13 -0
- package/dist/web/admin/store.d.ts.map +1 -0
- package/dist/web/admin/store.js +23 -0
- package/dist/web/admin/store.js.map +1 -0
- package/dist/web/admin/types.d.ts +28 -0
- package/dist/web/admin/types.d.ts.map +1 -0
- package/dist/web/admin/types.js +2 -0
- package/dist/web/admin/types.js.map +1 -0
- package/dist/web/login/oauth.d.ts +6 -0
- package/dist/web/login/oauth.d.ts.map +1 -0
- package/dist/{login/index.js → web/login/oauth.js} +107 -106
- package/dist/web/login/oauth.js.map +1 -0
- package/dist/{login → web/login}/portal.d.ts +5 -5
- package/dist/web/login/portal.d.ts.map +1 -0
- package/dist/{login → web/login}/portal.js +16 -35
- package/dist/web/login/portal.js.map +1 -0
- package/dist/web/login/store.d.ts +12 -0
- package/dist/web/login/store.d.ts.map +1 -0
- package/dist/web/login/store.js +28 -0
- package/dist/web/login/store.js.map +1 -0
- package/dist/web/login/types.d.ts +50 -0
- package/dist/web/login/types.d.ts.map +1 -0
- package/dist/web/login/types.js +2 -0
- package/dist/web/login/types.js.map +1 -0
- package/dist/web/session-view/command.d.ts +4 -0
- package/dist/web/session-view/command.d.ts.map +1 -0
- package/dist/{session-view → web/session-view}/command.js +1 -1
- package/dist/web/session-view/command.js.map +1 -0
- package/dist/{session-view → web/session-view}/portal.d.ts +2 -5
- package/dist/web/session-view/portal.d.ts.map +1 -0
- package/dist/{session-view → web/session-view}/portal.js +5 -5
- package/dist/web/session-view/portal.js.map +1 -0
- package/dist/web/session-view/service.d.ts +6 -0
- package/dist/web/session-view/service.d.ts.map +1 -0
- package/dist/{session-view → web/session-view}/service.js +6 -36
- package/dist/web/session-view/service.js.map +1 -0
- package/dist/web/session-view/store.d.ts +8 -0
- package/dist/web/session-view/store.d.ts.map +1 -0
- package/dist/web/session-view/store.js +20 -0
- package/dist/web/session-view/store.js.map +1 -0
- package/dist/{session-view/service.d.ts → web/session-view/types.d.ts} +20 -4
- package/dist/web/session-view/types.d.ts.map +1 -0
- package/dist/web/session-view/types.js +2 -0
- package/dist/web/session-view/types.js.map +1 -0
- package/dist/web/token-store.d.ts +19 -0
- package/dist/web/token-store.d.ts.map +1 -0
- package/dist/web/token-store.js +45 -0
- package/dist/web/token-store.js.map +1 -0
- package/dist/web/types.d.ts +5 -0
- package/dist/web/types.d.ts.map +1 -0
- package/dist/web/types.js +2 -0
- package/dist/web/types.js.map +1 -0
- package/package.json +1 -1
- package/dist/adapters/discord/index.d.ts +0 -3
- package/dist/adapters/discord/index.d.ts.map +0 -1
- package/dist/adapters/discord/index.js +0 -3
- package/dist/adapters/discord/index.js.map +0 -1
- package/dist/adapters/slack/index.d.ts +0 -3
- package/dist/adapters/slack/index.d.ts.map +0 -1
- package/dist/adapters/slack/index.js +0 -3
- package/dist/adapters/slack/index.js.map +0 -1
- package/dist/adapters/slack/thread-manager.d.ts +0 -19
- package/dist/adapters/slack/thread-manager.d.ts.map +0 -1
- package/dist/adapters/slack/thread-manager.js +0 -11
- package/dist/adapters/slack/thread-manager.js.map +0 -1
- package/dist/adapters/telegram/index.d.ts +0 -3
- package/dist/adapters/telegram/index.d.ts.map +0 -1
- package/dist/adapters/telegram/index.js +0 -3
- package/dist/adapters/telegram/index.js.map +0 -1
- package/dist/admin/portal.d.ts +0 -27
- package/dist/admin/portal.d.ts.map +0 -1
- package/dist/admin/portal.js.map +0 -1
- package/dist/admin/store.d.ts +0 -22
- package/dist/admin/store.d.ts.map +0 -1
- package/dist/admin/store.js +0 -39
- package/dist/admin/store.js.map +0 -1
- package/dist/commands/index.d.ts +0 -5
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js +0 -20
- package/dist/commands/index.js.map +0 -1
- package/dist/env.d.ts.map +0 -1
- package/dist/env.js.map +0 -1
- package/dist/file-guards.d.ts.map +0 -1
- package/dist/file-guards.js.map +0 -1
- package/dist/fs-atomic.d.ts.map +0 -1
- package/dist/fs-atomic.js.map +0 -1
- package/dist/html.d.ts.map +0 -1
- package/dist/html.js.map +0 -1
- package/dist/instrument.d.ts.map +0 -1
- package/dist/instrument.js.map +0 -1
- package/dist/login/index.d.ts +0 -43
- package/dist/login/index.d.ts.map +0 -1
- package/dist/login/index.js.map +0 -1
- package/dist/login/portal.d.ts.map +0 -1
- package/dist/login/portal.js.map +0 -1
- package/dist/login/store.d.ts +0 -26
- package/dist/login/store.d.ts.map +0 -1
- package/dist/login/store.js +0 -56
- package/dist/login/store.js.map +0 -1
- package/dist/runtime/index.d.ts +0 -2
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js +0 -2
- package/dist/runtime/index.js.map +0 -1
- package/dist/sentry.d.ts.map +0 -1
- package/dist/sentry.js.map +0 -1
- package/dist/session-view/command.d.ts +0 -5
- package/dist/session-view/command.d.ts.map +0 -1
- package/dist/session-view/command.js.map +0 -1
- package/dist/session-view/portal.d.ts.map +0 -1
- package/dist/session-view/portal.js.map +0 -1
- package/dist/session-view/service.d.ts.map +0 -1
- package/dist/session-view/service.js.map +0 -1
- package/dist/session-view/store.d.ts +0 -18
- package/dist/session-view/store.d.ts.map +0 -1
- package/dist/session-view/store.js +0 -36
- package/dist/session-view/store.js.map +0 -1
- package/dist/ui-copy.d.ts.map +0 -1
- package/dist/ui-copy.js.map +0 -1
- package/dist/vault-routing.d.ts.map +0 -1
- package/dist/vault-routing.js.map +0 -1
- package/dist/vault.d.ts.map +0 -1
- package/dist/vault.js.map +0 -1
- /package/dist/{instrument.d.ts → observability/instrument.d.ts} +0 -0
- /package/dist/{env.d.ts → utils/env.d.ts} +0 -0
- /package/dist/{env.js → utils/env.js} +0 -0
- /package/dist/{file-guards.d.ts → utils/file-guards.d.ts} +0 -0
- /package/dist/{file-guards.js → utils/file-guards.js} +0 -0
- /package/dist/{fs-atomic.d.ts → utils/fs-atomic.d.ts} +0 -0
- /package/dist/{fs-atomic.js → utils/fs-atomic.js} +0 -0
- /package/dist/{html.d.ts → utils/html.d.ts} +0 -0
- /package/dist/{html.js → utils/html.js} +0 -0
package/dist/tools/event.js
CHANGED
|
@@ -1,15 +1,81 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import { constants } from "node:fs";
|
|
54
|
+
import { mkdir, open, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
|
|
55
|
+
import { basename, join } from "node:path";
|
|
3
56
|
import { Type } from "@sinclair/typebox";
|
|
4
57
|
import * as log from "../log.js";
|
|
5
58
|
const eventSchema = Type.Object({
|
|
6
|
-
|
|
59
|
+
action: Type.Optional(Type.Union([
|
|
60
|
+
Type.Literal("create"),
|
|
61
|
+
Type.Literal("list"),
|
|
62
|
+
Type.Literal("read"),
|
|
63
|
+
Type.Literal("update"),
|
|
64
|
+
Type.Literal("delete"),
|
|
65
|
+
], { description: "CRUD action. Defaults to create for backward compatibility." })),
|
|
66
|
+
filename: Type.Optional(Type.String({
|
|
67
|
+
description: "Event filename for read, update, or delete actions",
|
|
68
|
+
})),
|
|
69
|
+
scope: Type.Optional(Type.Union([Type.Literal("conversation"), Type.Literal("all")], {
|
|
70
|
+
description: "List scope. Defaults to conversation, which only shows events for the current conversation. Use all only when the user explicitly asks for all events.",
|
|
71
|
+
})),
|
|
72
|
+
label: Type.Optional(Type.String({
|
|
7
73
|
description: "Brief description of the event you're scheduling (shown to user)",
|
|
8
|
-
}),
|
|
9
|
-
type: Type.Union([Type.Literal("immediate"), Type.Literal("one-shot"), Type.Literal("periodic")]),
|
|
10
|
-
text: Type.String({
|
|
74
|
+
})),
|
|
75
|
+
type: Type.Optional(Type.Union([Type.Literal("immediate"), Type.Literal("one-shot"), Type.Literal("periodic")])),
|
|
76
|
+
text: Type.Optional(Type.String({
|
|
11
77
|
description: "A self-contained task for the future run. Include the necessary context, tone, and constraints in the text itself because events do not inherit normal conversation history.",
|
|
12
|
-
}),
|
|
78
|
+
})),
|
|
13
79
|
at: Type.Optional(Type.String({
|
|
14
80
|
description: "ISO 8601 timestamp with offset, required for one-shot events",
|
|
15
81
|
})),
|
|
@@ -31,9 +97,59 @@ export class HostEventStore {
|
|
|
31
97
|
return new HostEventStore(join(workspaceDir, "events"));
|
|
32
98
|
}
|
|
33
99
|
async write(filename, payload) {
|
|
100
|
+
return this.writePayload(filename, payload);
|
|
101
|
+
}
|
|
102
|
+
async list() {
|
|
103
|
+
await mkdir(this.eventsDir, { recursive: true });
|
|
104
|
+
const entries = await readdir(this.eventsDir, { withFileTypes: true });
|
|
105
|
+
const events = await Promise.all(entries
|
|
106
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
|
|
107
|
+
.map(async (entry) => this.read(entry.name)));
|
|
108
|
+
return events.toSorted((a, b) => a.filename.localeCompare(b.filename));
|
|
109
|
+
}
|
|
110
|
+
async read(filename) {
|
|
111
|
+
const safeFilename = validateEventFilename(filename);
|
|
112
|
+
const filePath = join(this.eventsDir, safeFilename);
|
|
113
|
+
const [raw, fileStat] = await Promise.all([readFile(filePath, "utf-8"), stat(filePath)]);
|
|
114
|
+
const parsed = JSON.parse(raw);
|
|
115
|
+
return {
|
|
116
|
+
filename: safeFilename,
|
|
117
|
+
payload: parsed,
|
|
118
|
+
size: fileStat.size,
|
|
119
|
+
mtimeMs: fileStat.mtimeMs,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
async update(filename, payload) {
|
|
123
|
+
return this.writePayload(filename, payload, constants.O_WRONLY | constants.O_TRUNC);
|
|
124
|
+
}
|
|
125
|
+
async delete(filename) {
|
|
126
|
+
const safeFilename = validateEventFilename(filename);
|
|
127
|
+
await rm(join(this.eventsDir, safeFilename), { force: true });
|
|
128
|
+
return { deleted: true };
|
|
129
|
+
}
|
|
130
|
+
async writePayload(filename, payload, flag) {
|
|
34
131
|
await mkdir(this.eventsDir, { recursive: true });
|
|
35
|
-
const
|
|
36
|
-
|
|
132
|
+
const safeFilename = validateEventFilename(filename);
|
|
133
|
+
const filePath = join(this.eventsDir, safeFilename);
|
|
134
|
+
if (flag === undefined) {
|
|
135
|
+
await writeFile(filePath, JSON.stringify(payload) + "\n", "utf-8");
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
139
|
+
try {
|
|
140
|
+
const file = __addDisposableResource(env_1, await open(filePath, flag), true);
|
|
141
|
+
await file.writeFile(JSON.stringify(payload) + "\n", "utf-8");
|
|
142
|
+
}
|
|
143
|
+
catch (e_1) {
|
|
144
|
+
env_1.error = e_1;
|
|
145
|
+
env_1.hasError = true;
|
|
146
|
+
}
|
|
147
|
+
finally {
|
|
148
|
+
const result_1 = __disposeResources(env_1);
|
|
149
|
+
if (result_1)
|
|
150
|
+
await result_1;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
37
153
|
const fileStat = await stat(filePath);
|
|
38
154
|
return { path: filePath, size: fileStat.size };
|
|
39
155
|
}
|
|
@@ -43,7 +159,7 @@ export function createEventTool(eventStore) {
|
|
|
43
159
|
const tool = {
|
|
44
160
|
name: "event",
|
|
45
161
|
label: "event",
|
|
46
|
-
description: "
|
|
162
|
+
description: "CRUD tool for scheduled events. Create immediate, one-shot, or periodic events for the current conversation. List defaults to events for the current conversation only; use scope=all only when the user explicitly asks to list all events. Event text must be self-contained because events do not inherit normal conversation history.",
|
|
47
163
|
parameters: eventSchema,
|
|
48
164
|
execute: async (_toolCallId, params, signal) => {
|
|
49
165
|
if (signal?.aborted) {
|
|
@@ -52,29 +168,60 @@ export function createEventTool(eventStore) {
|
|
|
52
168
|
if (!eventContext) {
|
|
53
169
|
throw new Error("Event context not configured");
|
|
54
170
|
}
|
|
171
|
+
const action = params.action ?? "create";
|
|
172
|
+
if (action === "list") {
|
|
173
|
+
const events = await eventStore.list();
|
|
174
|
+
const conversationId = eventContext.conversationId;
|
|
175
|
+
const scopedEvents = params.scope === "all"
|
|
176
|
+
? events
|
|
177
|
+
: events.filter((event) => event.payload.conversationId === conversationId);
|
|
178
|
+
return {
|
|
179
|
+
content: [
|
|
180
|
+
{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: JSON.stringify({
|
|
183
|
+
scope: params.scope ?? "conversation",
|
|
184
|
+
conversationId: params.scope === "all" ? undefined : conversationId,
|
|
185
|
+
events: scopedEvents,
|
|
186
|
+
}, null, 2),
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
details: undefined,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (action === "read") {
|
|
193
|
+
const filename = requireFilename(params);
|
|
194
|
+
const event = await eventStore.read(filename);
|
|
195
|
+
return {
|
|
196
|
+
content: [{ type: "text", text: JSON.stringify(event, null, 2) }],
|
|
197
|
+
details: undefined,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (action === "delete") {
|
|
201
|
+
const filename = requireFilename(params);
|
|
202
|
+
await eventStore.delete(filename);
|
|
203
|
+
return {
|
|
204
|
+
content: [{ type: "text", text: `Deleted event ${filename}` }],
|
|
205
|
+
details: undefined,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
55
208
|
const payload = buildEventPayload(params, eventContext);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
209
|
+
const filename = action === "update"
|
|
210
|
+
? requireFilename(params)
|
|
211
|
+
: `${sanitizeFileSegment(params.filenamePrefix || payload.type || "event")}-${Date.now()}.json`;
|
|
212
|
+
log.logInfo(`${action === "update" ? "Updating" : "Writing"} event file via control plane store: ${filename} (type=${payload.type}, platform=${payload.platform}, conversation=${payload.conversationId})`);
|
|
59
213
|
try {
|
|
60
|
-
const result =
|
|
61
|
-
|
|
214
|
+
const result = action === "update"
|
|
215
|
+
? await eventStore.update(filename, payload)
|
|
216
|
+
: await eventStore.write(filename, payload);
|
|
217
|
+
log.logInfo(`${action === "update" ? "Updated" : "Wrote"} event file via control plane store: ${result.path} (${result.size} bytes)`);
|
|
62
218
|
}
|
|
63
219
|
catch (err) {
|
|
64
|
-
log.logWarning(`Failed to write event file via control plane store: ${filename}`, String(err));
|
|
220
|
+
log.logWarning(`Failed to ${action === "update" ? "update" : "write"} event file via control plane store: ${filename}`, String(err));
|
|
65
221
|
throw err;
|
|
66
222
|
}
|
|
67
223
|
return {
|
|
68
|
-
content: [
|
|
69
|
-
{
|
|
70
|
-
type: "text",
|
|
71
|
-
text: payload.type === "periodic"
|
|
72
|
-
? `Scheduled periodic event ${filename} for ${payload.platform}/${payload.conversationId} (${payload.schedule} ${payload.timezone})`
|
|
73
|
-
: payload.type === "one-shot"
|
|
74
|
-
? `Scheduled one-shot event ${filename} for ${payload.platform}/${payload.conversationId} at ${payload.at}`
|
|
75
|
-
: `Queued immediate event ${filename} for ${payload.platform}/${payload.conversationId}`,
|
|
76
|
-
},
|
|
77
|
-
],
|
|
224
|
+
content: [{ type: "text", text: formatEventWriteResult(action, filename, payload) }],
|
|
78
225
|
details: undefined,
|
|
79
226
|
};
|
|
80
227
|
},
|
|
@@ -87,6 +234,12 @@ export function createEventTool(eventStore) {
|
|
|
87
234
|
};
|
|
88
235
|
}
|
|
89
236
|
function buildEventPayload(params, context) {
|
|
237
|
+
if (!params.type) {
|
|
238
|
+
throw new Error("`type` is required for create and update actions");
|
|
239
|
+
}
|
|
240
|
+
if (!params.text) {
|
|
241
|
+
throw new Error("`text` is required for create and update actions");
|
|
242
|
+
}
|
|
90
243
|
const base = {
|
|
91
244
|
platform: context.platform,
|
|
92
245
|
conversationId: context.conversationId,
|
|
@@ -127,6 +280,34 @@ function buildEventPayload(params, context) {
|
|
|
127
280
|
timezone: params.timezone,
|
|
128
281
|
};
|
|
129
282
|
}
|
|
283
|
+
function formatEventWriteResult(action, filename, payload) {
|
|
284
|
+
const scheduledVerb = action === "update" ? "Updated" : "Scheduled";
|
|
285
|
+
const immediateVerb = action === "update" ? "Updated" : "Queued";
|
|
286
|
+
switch (payload.type) {
|
|
287
|
+
case "periodic":
|
|
288
|
+
return `${scheduledVerb} periodic event ${filename} for ${payload.platform}/${payload.conversationId} (${payload.schedule} ${payload.timezone})`;
|
|
289
|
+
case "one-shot":
|
|
290
|
+
return `${scheduledVerb} one-shot event ${filename} for ${payload.platform}/${payload.conversationId} at ${payload.at}`;
|
|
291
|
+
case "immediate":
|
|
292
|
+
return `${immediateVerb} immediate event ${filename} for ${payload.platform}/${payload.conversationId}`;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function requireFilename(params) {
|
|
296
|
+
if (!params.filename) {
|
|
297
|
+
throw new Error("`filename` is required for read, update, and delete actions");
|
|
298
|
+
}
|
|
299
|
+
return validateEventFilename(params.filename);
|
|
300
|
+
}
|
|
301
|
+
function validateEventFilename(filename) {
|
|
302
|
+
const trimmed = filename.trim();
|
|
303
|
+
if (!trimmed ||
|
|
304
|
+
trimmed !== basename(trimmed) ||
|
|
305
|
+
trimmed.includes("..") ||
|
|
306
|
+
!trimmed.endsWith(".json")) {
|
|
307
|
+
throw new Error("Invalid event filename");
|
|
308
|
+
}
|
|
309
|
+
return trimmed;
|
|
310
|
+
}
|
|
130
311
|
function sanitizeFileSegment(value) {
|
|
131
312
|
const sanitized = value
|
|
132
313
|
.trim()
|
package/dist/tools/event.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/tools/event.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, stat, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport * as log from \"../log.js\";\n\nconst eventSchema = Type.Object({\n label: Type.String({\n description: \"Brief description of the event you're scheduling (shown to user)\",\n }),\n type: Type.Union([Type.Literal(\"immediate\"), Type.Literal(\"one-shot\"), Type.Literal(\"periodic\")]),\n text: Type.String({\n description:\n \"A self-contained task for the future run. Include the necessary context, tone, and constraints in the text itself because events do not inherit normal conversation history.\",\n }),\n at: Type.Optional(\n Type.String({\n description: \"ISO 8601 timestamp with offset, required for one-shot events\",\n }),\n ),\n schedule: Type.Optional(\n Type.String({\n description: \"Cron schedule, required for periodic events\",\n }),\n ),\n timezone: Type.Optional(\n Type.String({\n description: \"IANA timezone, required for periodic events\",\n }),\n ),\n filenamePrefix: Type.Optional(\n Type.String({\n description: \"Optional filename prefix for the event file\",\n }),\n ),\n});\n\ninterface EventToolContext {\n platform: string;\n conversationId: string;\n conversationKind: \"direct\" | \"shared\";\n userId: string;\n}\n\ntype EventToolParams = {\n label: string;\n type: \"immediate\" | \"one-shot\" | \"periodic\";\n text: string;\n at?: string;\n schedule?: string;\n timezone?: string;\n filenamePrefix?: string;\n};\n\nexport type EventPayload =\n | {\n type: \"immediate\";\n platform: string;\n conversationId: string;\n conversationKind: \"direct\" | \"shared\";\n userId: string;\n text: string;\n }\n | {\n type: \"one-shot\";\n platform: string;\n conversationId: string;\n conversationKind: \"direct\" | \"shared\";\n userId: string;\n text: string;\n at: string;\n }\n | {\n type: \"periodic\";\n platform: string;\n conversationId: string;\n conversationKind: \"direct\" | \"shared\";\n userId: string;\n text: string;\n schedule: string;\n timezone: string;\n };\n\nexport interface EventStore {\n write(filename: string, payload: EventPayload): Promise<{ path: string; size: number }>;\n}\n\nexport class HostEventStore implements EventStore {\n constructor(private readonly eventsDir: string) {}\n\n static fromWorkspaceDir(workspaceDir: string): HostEventStore {\n return new HostEventStore(join(workspaceDir, \"events\"));\n }\n\n async write(filename: string, payload: EventPayload): Promise<{ path: string; size: number }> {\n await mkdir(this.eventsDir, { recursive: true });\n const filePath = join(this.eventsDir, filename);\n await writeFile(filePath, JSON.stringify(payload) + \"\\n\", \"utf-8\");\n const fileStat = await stat(filePath);\n return { path: filePath, size: fileStat.size };\n }\n}\n\nexport function createEventTool(eventStore: EventStore): {\n tool: AgentTool<typeof eventSchema>;\n setEventContext: (context: EventToolContext) => void;\n} {\n let eventContext: EventToolContext | null = null;\n\n const tool: AgentTool<typeof eventSchema> = {\n name: \"event\",\n label: \"event\",\n description:\n \"Schedule an immediate, one-shot, or periodic event for the current conversation. Write text as a self-contained task with any needed context, tone, or constraints because events do not inherit normal conversation history. This automatically writes to the correct events directory and fills the current platform, conversation, conversation kind, and requester userId.\",\n parameters: eventSchema,\n execute: async (_toolCallId: string, params: EventToolParams, signal?: AbortSignal) => {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!eventContext) {\n throw new Error(\"Event context not configured\");\n }\n\n const payload = buildEventPayload(params, eventContext);\n const prefix = sanitizeFileSegment(params.filenamePrefix || payload.type || \"event\");\n const filename = `${prefix}-${Date.now()}.json`;\n\n log.logInfo(\n `Writing event file via control plane store: ${filename} (type=${payload.type}, platform=${payload.platform}, conversation=${payload.conversationId})`,\n );\n\n try {\n const result = await eventStore.write(filename, payload);\n log.logInfo(\n `Wrote event file via control plane store: ${result.path} (${result.size} bytes)`,\n );\n } catch (err) {\n log.logWarning(\n `Failed to write event file via control plane store: ${filename}`,\n String(err),\n );\n throw err;\n }\n\n return {\n content: [\n {\n type: \"text\",\n text:\n payload.type === \"periodic\"\n ? `Scheduled periodic event ${filename} for ${payload.platform}/${payload.conversationId} (${payload.schedule} ${payload.timezone})`\n : payload.type === \"one-shot\"\n ? `Scheduled one-shot event ${filename} for ${payload.platform}/${payload.conversationId} at ${payload.at}`\n : `Queued immediate event ${filename} for ${payload.platform}/${payload.conversationId}`,\n },\n ],\n details: undefined,\n };\n },\n };\n\n return {\n tool,\n setEventContext: (context: EventToolContext) => {\n eventContext = context;\n },\n };\n}\n\nfunction buildEventPayload(params: EventToolParams, context: EventToolContext): EventPayload {\n const base = {\n platform: context.platform,\n conversationId: context.conversationId,\n conversationKind: context.conversationKind,\n userId: context.userId,\n text: params.text,\n };\n\n if (params.type === \"immediate\") {\n return {\n ...base,\n type: \"immediate\",\n };\n }\n\n if (params.type === \"one-shot\") {\n if (!params.at) {\n throw new Error(\"`at` is required for one-shot events\");\n }\n\n const atTime = new Date(params.at).getTime();\n if (Number.isNaN(atTime)) {\n throw new Error(\"`at` must be a valid ISO 8601 timestamp with UTC offset\");\n }\n if (atTime <= Date.now()) {\n throw new Error(\n `\\`at\\` must be in the future; got ${params.at} (now=${new Date().toISOString()}). Check the timezone offset.`,\n );\n }\n\n // No sessionKey or threadTs: reminders should fire as top-level messages, not buried in old threads\n return { ...base, type: \"one-shot\", at: params.at };\n }\n\n if (!params.schedule) {\n throw new Error(\"`schedule` is required for periodic events\");\n }\n if (!params.timezone) {\n throw new Error(\"`timezone` is required for periodic events\");\n }\n return {\n ...base,\n type: \"periodic\",\n schedule: params.schedule,\n timezone: params.timezone,\n };\n}\n\nfunction sanitizeFileSegment(value: string): string {\n const sanitized = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"event\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/tools/event.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { mkdir, open, readdir, readFile, rm, stat, writeFile } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { ConversationKind } from \"../adapter.js\";\nimport * as log from \"../log.js\";\n\nconst eventSchema = Type.Object({\n action: Type.Optional(\n Type.Union(\n [\n Type.Literal(\"create\"),\n Type.Literal(\"list\"),\n Type.Literal(\"read\"),\n Type.Literal(\"update\"),\n Type.Literal(\"delete\"),\n ],\n { description: \"CRUD action. Defaults to create for backward compatibility.\" },\n ),\n ),\n filename: Type.Optional(\n Type.String({\n description: \"Event filename for read, update, or delete actions\",\n }),\n ),\n scope: Type.Optional(\n Type.Union([Type.Literal(\"conversation\"), Type.Literal(\"all\")], {\n description:\n \"List scope. Defaults to conversation, which only shows events for the current conversation. Use all only when the user explicitly asks for all events.\",\n }),\n ),\n label: Type.Optional(\n Type.String({\n description: \"Brief description of the event you're scheduling (shown to user)\",\n }),\n ),\n type: Type.Optional(\n Type.Union([Type.Literal(\"immediate\"), Type.Literal(\"one-shot\"), Type.Literal(\"periodic\")]),\n ),\n text: Type.Optional(\n Type.String({\n description:\n \"A self-contained task for the future run. Include the necessary context, tone, and constraints in the text itself because events do not inherit normal conversation history.\",\n }),\n ),\n at: Type.Optional(\n Type.String({\n description: \"ISO 8601 timestamp with offset, required for one-shot events\",\n }),\n ),\n schedule: Type.Optional(\n Type.String({\n description: \"Cron schedule, required for periodic events\",\n }),\n ),\n timezone: Type.Optional(\n Type.String({\n description: \"IANA timezone, required for periodic events\",\n }),\n ),\n filenamePrefix: Type.Optional(\n Type.String({\n description: \"Optional filename prefix for the event file\",\n }),\n ),\n});\n\ninterface EventToolContext {\n platform: string;\n conversationId: string;\n conversationKind: ConversationKind;\n userId: string;\n}\n\ntype EventToolParams = {\n action?: \"create\" | \"list\" | \"read\" | \"update\" | \"delete\";\n filename?: string;\n scope?: \"conversation\" | \"all\";\n label?: string;\n type?: \"immediate\" | \"one-shot\" | \"periodic\";\n text?: string;\n at?: string;\n schedule?: string;\n timezone?: string;\n filenamePrefix?: string;\n};\n\nexport type { EventPayload, EventStore } from \"./types.js\";\nimport type { EventPayload, EventStore } from \"./types.js\";\n\nexport class HostEventStore implements EventStore {\n constructor(private readonly eventsDir: string) {}\n\n static fromWorkspaceDir(workspaceDir: string): HostEventStore {\n return new HostEventStore(join(workspaceDir, \"events\"));\n }\n\n async write(filename: string, payload: EventPayload): Promise<{ path: string; size: number }> {\n return this.writePayload(filename, payload);\n }\n\n async list(): Promise<\n Array<{ filename: string; payload: EventPayload; size: number; mtimeMs: number }>\n > {\n await mkdir(this.eventsDir, { recursive: true });\n const entries = await readdir(this.eventsDir, { withFileTypes: true });\n const events = await Promise.all(\n entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\"))\n .map(async (entry) => this.read(entry.name)),\n );\n return events.toSorted((a, b) => a.filename.localeCompare(b.filename));\n }\n\n async read(\n filename: string,\n ): Promise<{ filename: string; payload: EventPayload; size: number; mtimeMs: number }> {\n const safeFilename = validateEventFilename(filename);\n const filePath = join(this.eventsDir, safeFilename);\n const [raw, fileStat] = await Promise.all([readFile(filePath, \"utf-8\"), stat(filePath)]);\n const parsed = JSON.parse(raw) as EventPayload;\n return {\n filename: safeFilename,\n payload: parsed,\n size: fileStat.size,\n mtimeMs: fileStat.mtimeMs,\n };\n }\n\n async update(filename: string, payload: EventPayload): Promise<{ path: string; size: number }> {\n return this.writePayload(filename, payload, constants.O_WRONLY | constants.O_TRUNC);\n }\n\n async delete(filename: string): Promise<{ deleted: boolean }> {\n const safeFilename = validateEventFilename(filename);\n await rm(join(this.eventsDir, safeFilename), { force: true });\n return { deleted: true };\n }\n\n private async writePayload(\n filename: string,\n payload: EventPayload,\n flag?: string | number,\n ): Promise<{ path: string; size: number }> {\n await mkdir(this.eventsDir, { recursive: true });\n const safeFilename = validateEventFilename(filename);\n const filePath = join(this.eventsDir, safeFilename);\n if (flag === undefined) {\n await writeFile(filePath, JSON.stringify(payload) + \"\\n\", \"utf-8\");\n } else {\n await using file = await open(filePath, flag);\n await file.writeFile(JSON.stringify(payload) + \"\\n\", \"utf-8\");\n }\n const fileStat = await stat(filePath);\n return { path: filePath, size: fileStat.size };\n }\n}\n\nexport function createEventTool(eventStore: EventStore): {\n tool: AgentTool<typeof eventSchema>;\n setEventContext: (context: EventToolContext) => void;\n} {\n let eventContext: EventToolContext | null = null;\n\n const tool: AgentTool<typeof eventSchema> = {\n name: \"event\",\n label: \"event\",\n description:\n \"CRUD tool for scheduled events. Create immediate, one-shot, or periodic events for the current conversation. List defaults to events for the current conversation only; use scope=all only when the user explicitly asks to list all events. Event text must be self-contained because events do not inherit normal conversation history.\",\n parameters: eventSchema,\n execute: async (_toolCallId: string, params: EventToolParams, signal?: AbortSignal) => {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!eventContext) {\n throw new Error(\"Event context not configured\");\n }\n\n const action = params.action ?? \"create\";\n\n if (action === \"list\") {\n const events = await eventStore.list();\n const conversationId = eventContext.conversationId;\n const scopedEvents =\n params.scope === \"all\"\n ? events\n : events.filter((event) => event.payload.conversationId === conversationId);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n scope: params.scope ?? \"conversation\",\n conversationId: params.scope === \"all\" ? undefined : conversationId,\n events: scopedEvents,\n },\n null,\n 2,\n ),\n },\n ],\n details: undefined,\n };\n }\n\n if (action === \"read\") {\n const filename = requireFilename(params);\n const event = await eventStore.read(filename);\n return {\n content: [{ type: \"text\", text: JSON.stringify(event, null, 2) }],\n details: undefined,\n };\n }\n\n if (action === \"delete\") {\n const filename = requireFilename(params);\n await eventStore.delete(filename);\n return {\n content: [{ type: \"text\", text: `Deleted event ${filename}` }],\n details: undefined,\n };\n }\n\n const payload = buildEventPayload(params, eventContext);\n const filename =\n action === \"update\"\n ? requireFilename(params)\n : `${sanitizeFileSegment(params.filenamePrefix || payload.type || \"event\")}-${Date.now()}.json`;\n\n log.logInfo(\n `${action === \"update\" ? \"Updating\" : \"Writing\"} event file via control plane store: ${filename} (type=${payload.type}, platform=${payload.platform}, conversation=${payload.conversationId})`,\n );\n\n try {\n const result =\n action === \"update\"\n ? await eventStore.update(filename, payload)\n : await eventStore.write(filename, payload);\n log.logInfo(\n `${action === \"update\" ? \"Updated\" : \"Wrote\"} event file via control plane store: ${result.path} (${result.size} bytes)`,\n );\n } catch (err) {\n log.logWarning(\n `Failed to ${action === \"update\" ? \"update\" : \"write\"} event file via control plane store: ${filename}`,\n String(err),\n );\n throw err;\n }\n\n return {\n content: [{ type: \"text\", text: formatEventWriteResult(action, filename, payload) }],\n details: undefined,\n };\n },\n };\n\n return {\n tool,\n setEventContext: (context: EventToolContext) => {\n eventContext = context;\n },\n };\n}\n\nfunction buildEventPayload(params: EventToolParams, context: EventToolContext): EventPayload {\n if (!params.type) {\n throw new Error(\"`type` is required for create and update actions\");\n }\n if (!params.text) {\n throw new Error(\"`text` is required for create and update actions\");\n }\n\n const base = {\n platform: context.platform,\n conversationId: context.conversationId,\n conversationKind: context.conversationKind,\n userId: context.userId,\n text: params.text,\n };\n\n if (params.type === \"immediate\") {\n return {\n ...base,\n type: \"immediate\",\n };\n }\n\n if (params.type === \"one-shot\") {\n if (!params.at) {\n throw new Error(\"`at` is required for one-shot events\");\n }\n\n const atTime = new Date(params.at).getTime();\n if (Number.isNaN(atTime)) {\n throw new Error(\"`at` must be a valid ISO 8601 timestamp with UTC offset\");\n }\n if (atTime <= Date.now()) {\n throw new Error(\n `\\`at\\` must be in the future; got ${params.at} (now=${new Date().toISOString()}). Check the timezone offset.`,\n );\n }\n\n // No sessionKey or threadTs: reminders should fire as top-level messages, not buried in old threads\n return { ...base, type: \"one-shot\", at: params.at };\n }\n\n if (!params.schedule) {\n throw new Error(\"`schedule` is required for periodic events\");\n }\n if (!params.timezone) {\n throw new Error(\"`timezone` is required for periodic events\");\n }\n return {\n ...base,\n type: \"periodic\",\n schedule: params.schedule,\n timezone: params.timezone,\n };\n}\n\nfunction formatEventWriteResult(\n action: \"create\" | \"update\",\n filename: string,\n payload: EventPayload,\n): string {\n const scheduledVerb = action === \"update\" ? \"Updated\" : \"Scheduled\";\n const immediateVerb = action === \"update\" ? \"Updated\" : \"Queued\";\n switch (payload.type) {\n case \"periodic\":\n return `${scheduledVerb} periodic event ${filename} for ${payload.platform}/${payload.conversationId} (${payload.schedule} ${payload.timezone})`;\n case \"one-shot\":\n return `${scheduledVerb} one-shot event ${filename} for ${payload.platform}/${payload.conversationId} at ${payload.at}`;\n case \"immediate\":\n return `${immediateVerb} immediate event ${filename} for ${payload.platform}/${payload.conversationId}`;\n }\n}\n\nfunction requireFilename(params: EventToolParams): string {\n if (!params.filename) {\n throw new Error(\"`filename` is required for read, update, and delete actions\");\n }\n return validateEventFilename(params.filename);\n}\n\nfunction validateEventFilename(filename: string): string {\n const trimmed = filename.trim();\n if (\n !trimmed ||\n trimmed !== basename(trimmed) ||\n trimmed.includes(\"..\") ||\n !trimmed.endsWith(\".json\")\n ) {\n throw new Error(\"Invalid event filename\");\n }\n return trimmed;\n}\n\nfunction sanitizeFileSegment(value: string): string {\n const sanitized = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"event\";\n}\n"]}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { TSchema } from "@sinclair/typebox";
|
|
3
|
-
import type { ChatResponseBlockKit } from "../adapter.js";
|
|
3
|
+
import type { ChatResponseBlockKit, ConversationKind } from "../adapter.js";
|
|
4
4
|
import type { DockerContainerManager } from "../provisioner.js";
|
|
5
5
|
import type { Executor, SandboxConfig } from "../sandbox/index.js";
|
|
6
6
|
export declare function createMikanTools(executor: Executor, workspaceDir: string, sandboxController?: {
|
|
@@ -13,7 +13,7 @@ export declare function createMikanTools(executor: Executor, workspaceDir: strin
|
|
|
13
13
|
setEventContext: (context: {
|
|
14
14
|
platform: string;
|
|
15
15
|
conversationId: string;
|
|
16
|
-
conversationKind:
|
|
16
|
+
conversationKind: ConversationKind;
|
|
17
17
|
userId: string;
|
|
18
18
|
}) => void;
|
|
19
19
|
setSandboxContext: (context: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAG5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAQnE,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,iBAAiB,CAAC,EAAE;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CAC5E,GACA;IACD,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5B,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACrF,2BAA2B,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAC7F,eAAe,EAAE,CAAC,OAAO,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;IACX,iBAAiB,EAAE,CAAC,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF,CAyBA","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport type { ChatResponseBlockKit, ConversationKind } from \"../adapter.js\";\nimport { createAttachTool } from \"../adapters/slack/tools/attach.js\";\nimport { createSlackBlockKitTool } from \"../adapters/slack/tools/block-kit.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { Executor, SandboxConfig } from \"../sandbox/index.js\";\nimport { createBashTool } from \"./bash.js\";\nimport { createEditTool } from \"./edit.js\";\nimport { createEventTool, HostEventStore } from \"./event.js\";\nimport { createReadTool } from \"./read.js\";\nimport { createSandboxTool } from \"./sandbox.js\";\nimport { createWriteTool } from \"./write.js\";\n\nexport function createMikanTools(\n executor: Executor,\n workspaceDir: string,\n sandboxController?: {\n sandbox: SandboxConfig;\n provisioner?: Pick<DockerContainerManager, \"getLimitStatus\" | \"setLimits\">;\n },\n): {\n tools: AgentTool<TSchema>[];\n setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;\n setBlockKitResponseFunction: (fn: (response: ChatResponseBlockKit) => Promise<void>) => void;\n setEventContext: (context: {\n platform: string;\n conversationId: string;\n conversationKind: ConversationKind;\n userId: string;\n }) => void;\n setSandboxContext: (context: { conversationId: string; userId: string }) => void;\n} {\n const { tool: attachTool, setUploadFunction } = createAttachTool();\n const { tool: slackBlockKitTool, setBlockKitResponseFunction } = createSlackBlockKitTool();\n const { tool: eventTool, setEventContext } = createEventTool(\n HostEventStore.fromWorkspaceDir(workspaceDir),\n );\n const { tool: sandboxTool, setSandboxContext } = createSandboxTool(\n sandboxController ?? { sandbox: executor.getSandboxConfig() },\n );\n return {\n tools: [\n createReadTool(executor),\n createBashTool(executor),\n createEditTool(executor),\n createWriteTool(executor),\n eventTool,\n sandboxTool,\n attachTool,\n slackBlockKitTool,\n ],\n setUploadFunction,\n setBlockKitResponseFunction,\n setEventContext,\n setSandboxContext,\n };\n}\n"]}
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,YAAoB,EACpB,iBAGC;IAaD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,GAAG,uBAAuB,EAAE,CAAC;IAC3F,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,eAAe,CAC1D,cAAc,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAC9C,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,CAChE,iBAAiB,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAC9D,CAAC;IACF,OAAO;QACL,KAAK,EAAE;YACL,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,CAAC;YACxB,eAAe,CAAC,QAAQ,CAAC;YACzB,SAAS;YACT,WAAW;YACX,UAAU;YACV,iBAAiB;SAClB;QACD,iBAAiB;QACjB,2BAA2B;QAC3B,eAAe;QACf,iBAAiB;KAClB,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport type { ChatResponseBlockKit } from \"../adapter.js\";\nimport { createAttachTool } from \"../adapters/slack/tools/attach.js\";\nimport { createSlackBlockKitTool } from \"../adapters/slack/tools/block-kit.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { Executor, SandboxConfig } from \"../sandbox/index.js\";\nimport { createBashTool } from \"./bash.js\";\nimport { createEditTool } from \"./edit.js\";\nimport { createEventTool, HostEventStore } from \"./event.js\";\nimport { createReadTool } from \"./read.js\";\nimport { createSandboxTool } from \"./sandbox.js\";\nimport { createWriteTool } from \"./write.js\";\n\nexport function createMikanTools(\n executor: Executor,\n workspaceDir: string,\n sandboxController?: {\n sandbox: SandboxConfig;\n provisioner?: Pick<DockerContainerManager, \"getLimitStatus\" | \"setLimits\">;\n },\n): {\n tools: AgentTool<TSchema>[];\n setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;\n setBlockKitResponseFunction: (fn: (response: ChatResponseBlockKit) => Promise<void>) => void;\n setEventContext: (context: {\n platform: string;\n conversationId: string;\n conversationKind:
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,YAAoB,EACpB,iBAGC;IAaD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,GAAG,uBAAuB,EAAE,CAAC;IAC3F,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,eAAe,CAC1D,cAAc,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAC9C,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,CAChE,iBAAiB,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAC9D,CAAC;IACF,OAAO;QACL,KAAK,EAAE;YACL,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,CAAC;YACxB,eAAe,CAAC,QAAQ,CAAC;YACzB,SAAS;YACT,WAAW;YACX,UAAU;YACV,iBAAiB;SAClB;QACD,iBAAiB;QACjB,2BAA2B;QAC3B,eAAe;QACf,iBAAiB;KAClB,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport type { ChatResponseBlockKit, ConversationKind } from \"../adapter.js\";\nimport { createAttachTool } from \"../adapters/slack/tools/attach.js\";\nimport { createSlackBlockKitTool } from \"../adapters/slack/tools/block-kit.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { Executor, SandboxConfig } from \"../sandbox/index.js\";\nimport { createBashTool } from \"./bash.js\";\nimport { createEditTool } from \"./edit.js\";\nimport { createEventTool, HostEventStore } from \"./event.js\";\nimport { createReadTool } from \"./read.js\";\nimport { createSandboxTool } from \"./sandbox.js\";\nimport { createWriteTool } from \"./write.js\";\n\nexport function createMikanTools(\n executor: Executor,\n workspaceDir: string,\n sandboxController?: {\n sandbox: SandboxConfig;\n provisioner?: Pick<DockerContainerManager, \"getLimitStatus\" | \"setLimits\">;\n },\n): {\n tools: AgentTool<TSchema>[];\n setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;\n setBlockKitResponseFunction: (fn: (response: ChatResponseBlockKit) => Promise<void>) => void;\n setEventContext: (context: {\n platform: string;\n conversationId: string;\n conversationKind: ConversationKind;\n userId: string;\n }) => void;\n setSandboxContext: (context: { conversationId: string; userId: string }) => void;\n} {\n const { tool: attachTool, setUploadFunction } = createAttachTool();\n const { tool: slackBlockKitTool, setBlockKitResponseFunction } = createSlackBlockKitTool();\n const { tool: eventTool, setEventContext } = createEventTool(\n HostEventStore.fromWorkspaceDir(workspaceDir),\n );\n const { tool: sandboxTool, setSandboxContext } = createSandboxTool(\n sandboxController ?? { sandbox: executor.getSandboxConfig() },\n );\n return {\n tools: [\n createReadTool(executor),\n createBashTool(executor),\n createEditTool(executor),\n createWriteTool(executor),\n eventTool,\n sandboxTool,\n attachTool,\n slackBlockKitTool,\n ],\n setUploadFunction,\n setBlockKitResponseFunction,\n setEventContext,\n setSandboxContext,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/tools/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,sBAAsB,EAAkB,MAAM,mBAAmB,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,QAAA,MAAM,aAAa;;;;EAcjB,CAAC;AAQH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CAC5E;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,qBAAqB,GAAG;IACpE,IAAI,EAAE,SAAS,CAAC,OAAO,aAAa,CAAC,CAAC;IACtC,iBAAiB,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC1D,CA+CA","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { DockerContainerManager, ResourceLimits } from \"../provisioner.js\";\nimport type { SandboxConfig } from \"../sandbox/index.js\";\nimport { resolveActorVaultKey } from \"../vault
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/tools/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,sBAAsB,EAAkB,MAAM,mBAAmB,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,QAAA,MAAM,aAAa;;;;EAcjB,CAAC;AAQH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CAC5E;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,qBAAqB,GAAG;IACpE,IAAI,EAAE,SAAS,CAAC,OAAO,aAAa,CAAC,CAAC;IACtC,iBAAiB,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC1D,CA+CA","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { DockerContainerManager, ResourceLimits } from \"../provisioner.js\";\nimport type { SandboxConfig } from \"../sandbox/index.js\";\nimport { resolveActorVaultKey } from \"../vault/routing.js\";\n\nconst sandboxSchema = Type.Object({\n action: Type.Union([Type.Literal(\"status\"), Type.Literal(\"set\")], {\n description: \"Use status to inspect current limits, or set to apply temporary limits.\",\n }),\n cpus: Type.Optional(\n Type.String({\n description: \"Docker CPU limit for action=set, for example '0.5', '1', or '2'.\",\n }),\n ),\n memory: Type.Optional(\n Type.String({\n description: \"Docker memory limit for action=set, for example '512m', '1g', or '4g'.\",\n }),\n ),\n});\n\ntype SandboxToolParams = {\n action: \"status\" | \"set\";\n cpus?: string;\n memory?: string;\n};\n\ninterface SandboxToolContext {\n userId: string;\n conversationId: string;\n}\n\ninterface SandboxToolController {\n sandbox: SandboxConfig;\n provisioner?: Pick<DockerContainerManager, \"getLimitStatus\" | \"setLimits\">;\n}\n\nexport function createSandboxTool(controller: SandboxToolController): {\n tool: AgentTool<typeof sandboxSchema>;\n setSandboxContext: (context: SandboxToolContext) => void;\n} {\n let sandboxContext: SandboxToolContext | null = null;\n\n const tool: AgentTool<typeof sandboxSchema> = {\n name: \"sandbox\",\n label: \"sandbox\",\n description:\n \"Inspect or temporarily set CPU/memory limits for the current managed image sandbox. Limits apply to this conversation's sandbox container and are cleared when the container stops.\",\n parameters: sandboxSchema,\n execute: async (_toolCallId: string, params: SandboxToolParams, signal?: AbortSignal) => {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n if (!sandboxContext) {\n throw new Error(\"Sandbox context not configured\");\n }\n if (controller.sandbox.type !== \"image\" || !controller.provisioner) {\n throw new Error(\"The sandbox tool only supports image:* managed sandboxes\");\n }\n\n const containerKey = resolveActorVaultKey(\n controller.sandbox,\n sandboxContext.userId,\n sandboxContext.conversationId,\n );\n\n if (params.action === \"set\") {\n const limits = normalizeLimits(params);\n const status = await controller.provisioner.setLimits(containerKey, limits);\n return textResult(\n `Updated sandbox limits for ${containerKey}: ${formatLimits(status.limits)}. These temporary limits are cleared when the sandbox container stops.`,\n );\n }\n\n const status = controller.provisioner.getLimitStatus(containerKey);\n return textResult(\n `Sandbox limits for ${containerKey}: ${formatLimits(status.limits)}${status.boosted ? \" (boosted)\" : \"\"}.`,\n );\n },\n };\n\n return {\n tool,\n setSandboxContext: (context: SandboxToolContext) => {\n sandboxContext = context;\n },\n };\n}\n\nfunction normalizeLimits(params: SandboxToolParams): ResourceLimits {\n const cpus = normalizeLimitValue(\"cpus\", params.cpus);\n const memory = normalizeLimitValue(\"memory\", params.memory);\n if (!cpus && !memory) {\n throw new Error(\"action=set requires cpus and/or memory\");\n }\n return { ...(cpus ? { cpus } : {}), ...(memory ? { memory } : {}) };\n}\n\nfunction normalizeLimitValue(name: string, value: string | undefined): string | undefined {\n if (value === undefined) return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n if (!/^[A-Za-z0-9_.-]+$/.test(trimmed)) {\n throw new Error(`${name} must not contain whitespace or shell metacharacters`);\n }\n return trimmed;\n}\n\nfunction formatLimits(limits: ResourceLimits | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n\nfunction textResult(text: string): {\n content: Array<{ type: \"text\"; text: string }>;\n details: undefined;\n} {\n return { content: [{ type: \"text\", text }], details: undefined };\n}\n"]}
|
package/dist/tools/sandbox.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
|
-
import { resolveActorVaultKey } from "../vault
|
|
2
|
+
import { resolveActorVaultKey } from "../vault/routing.js";
|
|
3
3
|
const sandboxSchema = Type.Object({
|
|
4
4
|
action: Type.Union([Type.Literal("status"), Type.Literal("set")], {
|
|
5
5
|
description: "Use status to inspect current limits, or set to apply temporary limits.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/tools/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { DockerContainerManager, ResourceLimits } from \"../provisioner.js\";\nimport type { SandboxConfig } from \"../sandbox/index.js\";\nimport { resolveActorVaultKey } from \"../vault
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/tools/sandbox.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAGzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC;IAChC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE;QAChE,WAAW,EAAE,yEAAyE;KACvF,CAAC;IACF,IAAI,EAAE,IAAI,CAAC,QAAQ,CACjB,IAAI,CAAC,MAAM,CAAC;QACV,WAAW,EAAE,kEAAkE;KAChF,CAAC,CACH;IACD,MAAM,EAAE,IAAI,CAAC,QAAQ,CACnB,IAAI,CAAC,MAAM,CAAC;QACV,WAAW,EAAE,wEAAwE;KACtF,CAAC,CACH;CACF,CAAC,CAAC;AAkBH,MAAM,UAAU,iBAAiB,CAAC,UAAiC;IAIjE,IAAI,cAAc,GAA8B,IAAI,CAAC;IAErD,MAAM,IAAI,GAAoC;QAC5C,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,qLAAqL;QACvL,UAAU,EAAE,aAAa;QACzB,OAAO,EAAE,KAAK,EAAE,WAAmB,EAAE,MAAyB,EAAE,MAAoB,EAAE,EAAE;YACtF,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YACvC,CAAC;YACD,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACpD,CAAC;YACD,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;gBACnE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;YAC9E,CAAC;YAED,MAAM,YAAY,GAAG,oBAAoB,CACvC,UAAU,CAAC,OAAO,EAClB,cAAc,CAAC,MAAM,EACrB,cAAc,CAAC,cAAc,CAC9B,CAAC;YAEF,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;gBAC5B,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;gBACvC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;gBAC5E,OAAO,UAAU,CACf,8BAA8B,YAAY,KAAK,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,wEAAwE,CACnJ,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,cAAc,CAAC,YAAY,CAAC,CAAC;YACnE,OAAO,UAAU,CACf,sBAAsB,YAAY,KAAK,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,CAC3G,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,OAAO;QACL,IAAI;QACJ,iBAAiB,EAAE,CAAC,OAA2B,EAAE,EAAE;YACjD,cAAc,GAAG,OAAO,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,MAAyB;IAChD,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5D,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AACtE,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAY,EAAE,KAAyB;IAClE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,sDAAsD,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,MAAkC;IACtD,OAAO,OAAO,MAAM,EAAE,IAAI,IAAI,WAAW,aAAa,MAAM,EAAE,MAAM,IAAI,WAAW,EAAE,CAAC;AACxF,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAI9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACnE,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { DockerContainerManager, ResourceLimits } from \"../provisioner.js\";\nimport type { SandboxConfig } from \"../sandbox/index.js\";\nimport { resolveActorVaultKey } from \"../vault/routing.js\";\n\nconst sandboxSchema = Type.Object({\n action: Type.Union([Type.Literal(\"status\"), Type.Literal(\"set\")], {\n description: \"Use status to inspect current limits, or set to apply temporary limits.\",\n }),\n cpus: Type.Optional(\n Type.String({\n description: \"Docker CPU limit for action=set, for example '0.5', '1', or '2'.\",\n }),\n ),\n memory: Type.Optional(\n Type.String({\n description: \"Docker memory limit for action=set, for example '512m', '1g', or '4g'.\",\n }),\n ),\n});\n\ntype SandboxToolParams = {\n action: \"status\" | \"set\";\n cpus?: string;\n memory?: string;\n};\n\ninterface SandboxToolContext {\n userId: string;\n conversationId: string;\n}\n\ninterface SandboxToolController {\n sandbox: SandboxConfig;\n provisioner?: Pick<DockerContainerManager, \"getLimitStatus\" | \"setLimits\">;\n}\n\nexport function createSandboxTool(controller: SandboxToolController): {\n tool: AgentTool<typeof sandboxSchema>;\n setSandboxContext: (context: SandboxToolContext) => void;\n} {\n let sandboxContext: SandboxToolContext | null = null;\n\n const tool: AgentTool<typeof sandboxSchema> = {\n name: \"sandbox\",\n label: \"sandbox\",\n description:\n \"Inspect or temporarily set CPU/memory limits for the current managed image sandbox. Limits apply to this conversation's sandbox container and are cleared when the container stops.\",\n parameters: sandboxSchema,\n execute: async (_toolCallId: string, params: SandboxToolParams, signal?: AbortSignal) => {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n if (!sandboxContext) {\n throw new Error(\"Sandbox context not configured\");\n }\n if (controller.sandbox.type !== \"image\" || !controller.provisioner) {\n throw new Error(\"The sandbox tool only supports image:* managed sandboxes\");\n }\n\n const containerKey = resolveActorVaultKey(\n controller.sandbox,\n sandboxContext.userId,\n sandboxContext.conversationId,\n );\n\n if (params.action === \"set\") {\n const limits = normalizeLimits(params);\n const status = await controller.provisioner.setLimits(containerKey, limits);\n return textResult(\n `Updated sandbox limits for ${containerKey}: ${formatLimits(status.limits)}. These temporary limits are cleared when the sandbox container stops.`,\n );\n }\n\n const status = controller.provisioner.getLimitStatus(containerKey);\n return textResult(\n `Sandbox limits for ${containerKey}: ${formatLimits(status.limits)}${status.boosted ? \" (boosted)\" : \"\"}.`,\n );\n },\n };\n\n return {\n tool,\n setSandboxContext: (context: SandboxToolContext) => {\n sandboxContext = context;\n },\n };\n}\n\nfunction normalizeLimits(params: SandboxToolParams): ResourceLimits {\n const cpus = normalizeLimitValue(\"cpus\", params.cpus);\n const memory = normalizeLimitValue(\"memory\", params.memory);\n if (!cpus && !memory) {\n throw new Error(\"action=set requires cpus and/or memory\");\n }\n return { ...(cpus ? { cpus } : {}), ...(memory ? { memory } : {}) };\n}\n\nfunction normalizeLimitValue(name: string, value: string | undefined): string | undefined {\n if (value === undefined) return undefined;\n const trimmed = value.trim();\n if (!trimmed) return undefined;\n if (!/^[A-Za-z0-9_.-]+$/.test(trimmed)) {\n throw new Error(`${name} must not contain whitespace or shell metacharacters`);\n }\n return trimmed;\n}\n\nfunction formatLimits(limits: ResourceLimits | undefined): string {\n return `CPU ${limits?.cpus ?? \"unlimited\"} / Memory ${limits?.memory ?? \"unlimited\"}`;\n}\n\nfunction textResult(text: string): {\n content: Array<{ type: \"text\"; text: string }>;\n details: undefined;\n} {\n return { content: [{ type: \"text\", text }], details: undefined };\n}\n"]}
|
package/dist/tools/truncate.d.ts
CHANGED
|
@@ -9,32 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export declare const DEFAULT_MAX_LINES = 2000;
|
|
11
11
|
export declare const DEFAULT_MAX_BYTES: number;
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
content: string;
|
|
15
|
-
/** Whether truncation occurred */
|
|
16
|
-
truncated: boolean;
|
|
17
|
-
/** Which limit was hit: "lines", "bytes", or null if not truncated */
|
|
18
|
-
truncatedBy: "lines" | "bytes" | null;
|
|
19
|
-
/** Total number of lines in the original content */
|
|
20
|
-
totalLines: number;
|
|
21
|
-
/** Total number of bytes in the original content */
|
|
22
|
-
totalBytes: number;
|
|
23
|
-
/** Number of complete lines in the truncated output */
|
|
24
|
-
outputLines: number;
|
|
25
|
-
/** Number of bytes in the truncated output */
|
|
26
|
-
outputBytes: number;
|
|
27
|
-
/** Whether the last line was partially truncated (only for tail truncation edge case) */
|
|
28
|
-
lastLinePartial: boolean;
|
|
29
|
-
/** Whether the first line exceeded the byte limit (for head truncation) */
|
|
30
|
-
firstLineExceedsLimit: boolean;
|
|
31
|
-
}
|
|
32
|
-
export interface TruncationOptions {
|
|
33
|
-
/** Maximum number of lines (default: 2000) */
|
|
34
|
-
maxLines?: number;
|
|
35
|
-
/** Maximum number of bytes (default: 50KB) */
|
|
36
|
-
maxBytes?: number;
|
|
37
|
-
}
|
|
12
|
+
export type { TruncationOptions, TruncationResult } from "./types.js";
|
|
13
|
+
import type { TruncationOptions, TruncationResult } from "./types.js";
|
|
38
14
|
/**
|
|
39
15
|
* Format bytes as human-readable size.
|
|
40
16
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../src/tools/truncate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAE3C,
|
|
1
|
+
{"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../../src/tools/truncate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAE3C,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEtE;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CA4E/F;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,iBAAsB,GAAG,gBAAgB,CAqE/F","sourcesContent":["/**\n * Shared truncation utilities for tool outputs.\n *\n * Truncation is based on two independent limits - whichever is hit first wins:\n * - Line limit (default: 2000 lines)\n * - Byte limit (default: 50KB)\n *\n * Never returns partial lines (except bash tail truncation edge case).\n */\n\nexport const DEFAULT_MAX_LINES = 2000;\nexport const DEFAULT_MAX_BYTES = 50 * 1024; // 50KB\n\nexport type { TruncationOptions, TruncationResult } from \"./types.js\";\nimport type { TruncationOptions, TruncationResult } from \"./types.js\";\n\n/**\n * Format bytes as human-readable size.\n */\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes}B`;\n } else if (bytes < 1024 * 1024) {\n return `${(bytes / 1024).toFixed(1)}KB`;\n } else {\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n }\n}\n\n/**\n * Truncate content from the head (keep first N lines/bytes).\n * Suitable for file reads where you want to see the beginning.\n *\n * Never returns partial lines. If first line exceeds byte limit,\n * returns empty content with firstLineExceedsLimit=true.\n */\nexport function truncateHead(content: string, options: TruncationOptions = {}): TruncationResult {\n const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\n const totalBytes = Buffer.byteLength(content, \"utf-8\");\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n\n // Check if no truncation needed\n if (totalLines <= maxLines && totalBytes <= maxBytes) {\n return {\n content,\n truncated: false,\n truncatedBy: null,\n totalLines,\n totalBytes,\n outputLines: totalLines,\n outputBytes: totalBytes,\n lastLinePartial: false,\n firstLineExceedsLimit: false,\n };\n }\n\n // Check if first line alone exceeds byte limit\n const firstLineBytes = Buffer.byteLength(lines[0], \"utf-8\");\n if (firstLineBytes > maxBytes) {\n return {\n content: \"\",\n truncated: true,\n truncatedBy: \"bytes\",\n totalLines,\n totalBytes,\n outputLines: 0,\n outputBytes: 0,\n lastLinePartial: false,\n firstLineExceedsLimit: true,\n };\n }\n\n // Collect complete lines that fit\n const outputLinesArr: string[] = [];\n let outputBytesCount = 0;\n let truncatedBy: \"lines\" | \"bytes\" = \"lines\";\n\n for (let i = 0; i < lines.length && i < maxLines; i++) {\n const line = lines[i];\n const lineBytes = Buffer.byteLength(line, \"utf-8\") + (i > 0 ? 1 : 0); // +1 for newline\n\n if (outputBytesCount + lineBytes > maxBytes) {\n truncatedBy = \"bytes\";\n break;\n }\n\n outputLinesArr.push(line);\n outputBytesCount += lineBytes;\n }\n\n // If we exited due to line limit\n if (outputLinesArr.length >= maxLines && outputBytesCount <= maxBytes) {\n truncatedBy = \"lines\";\n }\n\n const outputContent = outputLinesArr.join(\"\\n\");\n const finalOutputBytes = Buffer.byteLength(outputContent, \"utf-8\");\n\n return {\n content: outputContent,\n truncated: true,\n truncatedBy,\n totalLines,\n totalBytes,\n outputLines: outputLinesArr.length,\n outputBytes: finalOutputBytes,\n lastLinePartial: false,\n firstLineExceedsLimit: false,\n };\n}\n\n/**\n * Truncate content from the tail (keep last N lines/bytes).\n * Suitable for bash output where you want to see the end (errors, final results).\n *\n * May return partial first line if the last line of original content exceeds byte limit.\n */\nexport function truncateTail(content: string, options: TruncationOptions = {}): TruncationResult {\n const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n\n const totalBytes = Buffer.byteLength(content, \"utf-8\");\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n\n // Check if no truncation needed\n if (totalLines <= maxLines && totalBytes <= maxBytes) {\n return {\n content,\n truncated: false,\n truncatedBy: null,\n totalLines,\n totalBytes,\n outputLines: totalLines,\n outputBytes: totalBytes,\n lastLinePartial: false,\n firstLineExceedsLimit: false,\n };\n }\n\n // Work backwards from the end\n const outputLinesArr: string[] = [];\n let outputBytesCount = 0;\n let truncatedBy: \"lines\" | \"bytes\" = \"lines\";\n let lastLinePartial = false;\n\n for (let i = lines.length - 1; i >= 0 && outputLinesArr.length < maxLines; i--) {\n const line = lines[i];\n const lineBytes = Buffer.byteLength(line, \"utf-8\") + (outputLinesArr.length > 0 ? 1 : 0); // +1 for newline\n\n if (outputBytesCount + lineBytes > maxBytes) {\n truncatedBy = \"bytes\";\n // Edge case: if we haven't added ANY lines yet and this line exceeds maxBytes,\n // take the end of the line (partial)\n if (outputLinesArr.length === 0) {\n const truncatedLine = truncateStringToBytesFromEnd(line, maxBytes);\n outputLinesArr.unshift(truncatedLine);\n outputBytesCount = Buffer.byteLength(truncatedLine, \"utf-8\");\n lastLinePartial = true;\n }\n break;\n }\n\n outputLinesArr.unshift(line);\n outputBytesCount += lineBytes;\n }\n\n // If we exited due to line limit\n if (outputLinesArr.length >= maxLines && outputBytesCount <= maxBytes) {\n truncatedBy = \"lines\";\n }\n\n const outputContent = outputLinesArr.join(\"\\n\");\n const finalOutputBytes = Buffer.byteLength(outputContent, \"utf-8\");\n\n return {\n content: outputContent,\n truncated: true,\n truncatedBy,\n totalLines,\n totalBytes,\n outputLines: outputLinesArr.length,\n outputBytes: finalOutputBytes,\n lastLinePartial,\n firstLineExceedsLimit: false,\n };\n}\n\n/**\n * Truncate a string to fit within a byte limit (from the end).\n * Handles multi-byte UTF-8 characters correctly.\n */\nfunction truncateStringToBytesFromEnd(str: string, maxBytes: number): string {\n const buf = Buffer.from(str, \"utf-8\");\n if (buf.length <= maxBytes) {\n return str;\n }\n\n // Start from the end, skip maxBytes back\n let start = buf.length - maxBytes;\n\n // Find a valid UTF-8 boundary (start of a character)\n while (start < buf.length && (buf[start] & 0xc0) === 0x80) {\n start++;\n }\n\n return buf.slice(start).toString(\"utf-8\");\n}\n"]}
|