@holon-run/agentinbox 0.2.0 → 0.3.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/dist/src/cli.js +110 -0
- package/dist/src/current_agent.js +1 -0
- package/dist/src/http.js +157 -1
- package/dist/src/runtime_gate.js +464 -0
- package/dist/src/service.js +645 -7
- package/dist/src/store.js +105 -3
- package/dist/src/terminal.js +12 -4
- package/drizzle/migrations/0005_timers.sql +22 -0
- package/drizzle/migrations/0006_terminal_runtime_pid.sql +1 -0
- package/drizzle/schema.ts +21 -0
- package/package.json +1 -1
package/dist/src/cli.js
CHANGED
|
@@ -180,6 +180,7 @@ async function main() {
|
|
|
180
180
|
backend: detected.backend,
|
|
181
181
|
runtimeKind: detected.runtimeKind,
|
|
182
182
|
runtimeSessionId: detected.runtimeSessionId ?? undefined,
|
|
183
|
+
runtimePid: detected.runtimePid ?? undefined,
|
|
183
184
|
tmuxPaneId: detected.tmuxPaneId ?? undefined,
|
|
184
185
|
tty: detected.tty ?? undefined,
|
|
185
186
|
termProgram: detected.termProgram ?? undefined,
|
|
@@ -344,6 +345,62 @@ async function main() {
|
|
|
344
345
|
});
|
|
345
346
|
return;
|
|
346
347
|
}
|
|
348
|
+
if (command === "timer" && normalized[1] === "add") {
|
|
349
|
+
const args = normalized.slice(2);
|
|
350
|
+
const allowedFlags = ["--agent-id", "--at", "--every", "--cron", "--timezone", "--message", "--sender"];
|
|
351
|
+
if (positionalArgs(args, ["--agent-id", "--at", "--every", "--cron", "--timezone", "--message", "--sender"]).length > 0 || unexpectedFlags(args, allowedFlags).length > 0) {
|
|
352
|
+
throw new Error("usage: agentinbox timer add --agent-id ID (--at ISO8601 | --every DURATION | --cron EXPR) --message TEXT [--timezone TZ] [--sender SENDER]");
|
|
353
|
+
}
|
|
354
|
+
const agentId = takeFlagValue(normalized, "--agent-id");
|
|
355
|
+
const message = takeFlagValue(normalized, "--message");
|
|
356
|
+
const at = takeFlagValue(normalized, "--at");
|
|
357
|
+
const every = takeFlagValue(normalized, "--every");
|
|
358
|
+
const cron = takeFlagValue(normalized, "--cron");
|
|
359
|
+
if (!agentId || !message || [at, every, cron].filter(Boolean).length !== 1) {
|
|
360
|
+
throw new Error("usage: agentinbox timer add --agent-id ID (--at ISO8601 | --every DURATION | --cron EXPR) --message TEXT [--timezone TZ] [--sender SENDER]");
|
|
361
|
+
}
|
|
362
|
+
await printRemote(client, "/timers", {
|
|
363
|
+
agentId,
|
|
364
|
+
at: at ?? undefined,
|
|
365
|
+
every: every ? parseDurationMs(every) : undefined,
|
|
366
|
+
cron: cron ?? undefined,
|
|
367
|
+
timezone: takeFlagValue(normalized, "--timezone") ?? undefined,
|
|
368
|
+
message,
|
|
369
|
+
sender: takeFlagValue(normalized, "--sender") ?? undefined,
|
|
370
|
+
});
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (command === "timer" && normalized[1] === "list") {
|
|
374
|
+
const query = buildQuery({
|
|
375
|
+
agent_id: takeFlagValue(normalized, "--agent-id"),
|
|
376
|
+
});
|
|
377
|
+
await printRemote(client, `/timers${query}`, undefined, "GET");
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (command === "timer" && normalized[1] === "pause") {
|
|
381
|
+
const scheduleId = normalized[2];
|
|
382
|
+
if (!scheduleId) {
|
|
383
|
+
throw new Error("usage: agentinbox timer pause <scheduleId>");
|
|
384
|
+
}
|
|
385
|
+
await printRemote(client, `/timers/${encodeURIComponent(scheduleId)}/pause`, {});
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (command === "timer" && normalized[1] === "resume") {
|
|
389
|
+
const scheduleId = normalized[2];
|
|
390
|
+
if (!scheduleId) {
|
|
391
|
+
throw new Error("usage: agentinbox timer resume <scheduleId>");
|
|
392
|
+
}
|
|
393
|
+
await printRemote(client, `/timers/${encodeURIComponent(scheduleId)}/resume`, {});
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (command === "timer" && normalized[1] === "remove") {
|
|
397
|
+
const scheduleId = normalized[2];
|
|
398
|
+
if (!scheduleId) {
|
|
399
|
+
throw new Error("usage: agentinbox timer remove <scheduleId>");
|
|
400
|
+
}
|
|
401
|
+
await printRemote(client, `/timers/${encodeURIComponent(scheduleId)}`, undefined, "DELETE");
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
347
404
|
if (command === "inbox" && normalized[1] === "list") {
|
|
348
405
|
await printRemote(client, "/agents", undefined, "GET");
|
|
349
406
|
return;
|
|
@@ -374,6 +431,23 @@ async function main() {
|
|
|
374
431
|
console.log((0, util_1.jsonResponse)(withCommandMetadata(response.data, selection)));
|
|
375
432
|
return;
|
|
376
433
|
}
|
|
434
|
+
if (command === "inbox" && normalized[1] === "send") {
|
|
435
|
+
const args = normalized.slice(2);
|
|
436
|
+
const allowedFlags = ["--agent-id", "--message", "--sender"];
|
|
437
|
+
if (positionalArgs(args, ["--agent-id", "--message", "--sender"]).length > 0 || unexpectedFlags(args, allowedFlags).length > 0) {
|
|
438
|
+
throw new Error("usage: agentinbox inbox send --agent-id ID --message TEXT [--sender SENDER]");
|
|
439
|
+
}
|
|
440
|
+
const agentId = takeFlagValue(normalized, "--agent-id");
|
|
441
|
+
const message = takeFlagValue(normalized, "--message");
|
|
442
|
+
if (!agentId || !message) {
|
|
443
|
+
throw new Error("usage: agentinbox inbox send --agent-id ID --message TEXT [--sender SENDER]");
|
|
444
|
+
}
|
|
445
|
+
await printRemote(client, `/agents/${encodeURIComponent(agentId)}/inbox/items`, {
|
|
446
|
+
message,
|
|
447
|
+
sender: takeFlagValue(normalized, "--sender") ?? undefined,
|
|
448
|
+
});
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
377
451
|
if (command === "inbox" && normalized[1] === "watch") {
|
|
378
452
|
const args = normalized.slice(2);
|
|
379
453
|
if (positionalArgs(args, ["--agent-id", "--after-item", "--heartbeat-ms"]).length > 0) {
|
|
@@ -592,6 +666,7 @@ async function selectAgentForCommand(client, options) {
|
|
|
592
666
|
backend: contextForCurrent.backend,
|
|
593
667
|
runtimeKind: contextForCurrent.runtimeKind,
|
|
594
668
|
runtimeSessionId: contextForCurrent.runtimeSessionId ?? undefined,
|
|
669
|
+
runtimePid: contextForCurrent.runtimePid ?? undefined,
|
|
595
670
|
tmuxPaneId: contextForCurrent.tmuxPaneId ?? undefined,
|
|
596
671
|
tty: contextForCurrent.tty ?? undefined,
|
|
597
672
|
termProgram: contextForCurrent.termProgram ?? undefined,
|
|
@@ -648,6 +723,30 @@ function parseOptionalNumber(value) {
|
|
|
648
723
|
}
|
|
649
724
|
return parsed;
|
|
650
725
|
}
|
|
726
|
+
function parseDurationMs(value) {
|
|
727
|
+
if (/^\d+$/.test(value)) {
|
|
728
|
+
return Number(value);
|
|
729
|
+
}
|
|
730
|
+
const match = /^(\d+)(ms|s|m|h|d)$/.exec(value);
|
|
731
|
+
if (!match) {
|
|
732
|
+
throw new Error(`invalid duration: ${value}`);
|
|
733
|
+
}
|
|
734
|
+
const amount = Number(match[1]);
|
|
735
|
+
switch (match[2]) {
|
|
736
|
+
case "ms":
|
|
737
|
+
return amount;
|
|
738
|
+
case "s":
|
|
739
|
+
return amount * 1_000;
|
|
740
|
+
case "m":
|
|
741
|
+
return amount * 60_000;
|
|
742
|
+
case "h":
|
|
743
|
+
return amount * 60 * 60_000;
|
|
744
|
+
case "d":
|
|
745
|
+
return amount * 24 * 60 * 60_000;
|
|
746
|
+
default:
|
|
747
|
+
throw new Error(`invalid duration: ${value}`);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
651
750
|
function normalizeHelpArgs(args) {
|
|
652
751
|
if (args[0] !== "help") {
|
|
653
752
|
return args;
|
|
@@ -762,6 +861,7 @@ Commands:
|
|
|
762
861
|
daemon
|
|
763
862
|
source
|
|
764
863
|
agent
|
|
864
|
+
timer
|
|
765
865
|
subscription
|
|
766
866
|
inbox
|
|
767
867
|
gc
|
|
@@ -808,6 +908,15 @@ Usage:
|
|
|
808
908
|
agentinbox agent target add webhook <agentId> --url URL [--activation-mode MODE] [--notify-lease-ms N]
|
|
809
909
|
agentinbox agent target list <agentId>
|
|
810
910
|
agentinbox agent target remove <agentId> <targetId>
|
|
911
|
+
`,
|
|
912
|
+
timer: `agentinbox timer
|
|
913
|
+
|
|
914
|
+
Usage:
|
|
915
|
+
agentinbox timer add --agent-id ID (--at ISO8601 | --every DURATION | --cron EXPR) --message TEXT [--timezone TZ] [--sender SENDER]
|
|
916
|
+
agentinbox timer list [--agent-id ID]
|
|
917
|
+
agentinbox timer pause <scheduleId>
|
|
918
|
+
agentinbox timer resume <scheduleId>
|
|
919
|
+
agentinbox timer remove <scheduleId>
|
|
811
920
|
`,
|
|
812
921
|
subscription: `agentinbox subscription
|
|
813
922
|
|
|
@@ -826,6 +935,7 @@ Usage:
|
|
|
826
935
|
agentinbox inbox list
|
|
827
936
|
agentinbox inbox show <agentId>
|
|
828
937
|
agentinbox inbox read [--agent-id ID] [--after-item ID] [--include-acked]
|
|
938
|
+
agentinbox inbox send --agent-id ID --message TEXT [--sender SENDER]
|
|
829
939
|
agentinbox inbox watch [--agent-id ID] [--after-item ID] [--include-acked] [--heartbeat-ms N]
|
|
830
940
|
agentinbox inbox ack [--agent-id ID] (--through <itemId> | --item <itemId> | --all)
|
|
831
941
|
agentinbox inbox compact <agentId>
|
package/dist/src/http.js
CHANGED
|
@@ -419,6 +419,7 @@ function buildFastifyServer(service) {
|
|
|
419
419
|
backend: { type: "string", minLength: 1 },
|
|
420
420
|
runtimeKind: { type: "string" },
|
|
421
421
|
runtimeSessionId: { type: "string" },
|
|
422
|
+
runtimePid: { type: "integer", minimum: 1 },
|
|
422
423
|
tmuxPaneId: { type: "string" },
|
|
423
424
|
tty: { type: "string" },
|
|
424
425
|
termProgram: { type: "string" },
|
|
@@ -439,6 +440,7 @@ function buildFastifyServer(service) {
|
|
|
439
440
|
backend: String(body.backend),
|
|
440
441
|
runtimeKind: optionalString(body.runtimeKind),
|
|
441
442
|
runtimeSessionId: optionalString(body.runtimeSessionId),
|
|
443
|
+
runtimePid: typeof body.runtimePid === "number" ? body.runtimePid : null,
|
|
442
444
|
mode: "agent_prompt",
|
|
443
445
|
tmuxPaneId: optionalString(body.tmuxPaneId),
|
|
444
446
|
tty: optionalString(body.tty),
|
|
@@ -563,6 +565,123 @@ function buildFastifyServer(service) {
|
|
|
563
565
|
const params = request.params;
|
|
564
566
|
return service.removeActivationTarget(decodeURIComponent(params.agentId), decodeURIComponent(params.targetId));
|
|
565
567
|
});
|
|
568
|
+
app.get("/timers", {
|
|
569
|
+
schema: {
|
|
570
|
+
tags: ["timers"],
|
|
571
|
+
querystring: {
|
|
572
|
+
type: "object",
|
|
573
|
+
additionalProperties: false,
|
|
574
|
+
properties: {
|
|
575
|
+
agent_id: { type: "string" },
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
response: {
|
|
579
|
+
200: {
|
|
580
|
+
type: "object",
|
|
581
|
+
required: ["timers"],
|
|
582
|
+
properties: {
|
|
583
|
+
timers: { type: "array", items: jsonObjectSchema },
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
}, async (request) => {
|
|
589
|
+
const query = request.query;
|
|
590
|
+
return { timers: service.listTimers(query.agent_id) };
|
|
591
|
+
});
|
|
592
|
+
app.post("/timers", {
|
|
593
|
+
schema: {
|
|
594
|
+
tags: ["timers"],
|
|
595
|
+
body: {
|
|
596
|
+
type: "object",
|
|
597
|
+
additionalProperties: false,
|
|
598
|
+
required: ["agentId", "message"],
|
|
599
|
+
properties: {
|
|
600
|
+
agentId: { type: "string", minLength: 1 },
|
|
601
|
+
at: { type: "string", minLength: 1 },
|
|
602
|
+
every: { type: "integer", minimum: 60000 },
|
|
603
|
+
cron: { type: "string", minLength: 1 },
|
|
604
|
+
timezone: { type: "string", minLength: 1 },
|
|
605
|
+
message: { type: "string", minLength: 1 },
|
|
606
|
+
sender: { type: "string", minLength: 1 },
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
response: {
|
|
610
|
+
200: jsonObjectSchema,
|
|
611
|
+
400: errorResponseSchema,
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
}, async (request) => {
|
|
615
|
+
const body = request.body;
|
|
616
|
+
return service.registerTimer({
|
|
617
|
+
agentId: String(body.agentId),
|
|
618
|
+
at: optionalString(body.at) ?? null,
|
|
619
|
+
every: typeof body.every === "number" ? body.every : null,
|
|
620
|
+
cron: optionalString(body.cron) ?? null,
|
|
621
|
+
timezone: optionalString(body.timezone) ?? null,
|
|
622
|
+
message: String(body.message),
|
|
623
|
+
sender: optionalString(body.sender) ?? null,
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
app.post("/timers/:scheduleId/pause", {
|
|
627
|
+
schema: {
|
|
628
|
+
tags: ["timers"],
|
|
629
|
+
params: {
|
|
630
|
+
type: "object",
|
|
631
|
+
required: ["scheduleId"],
|
|
632
|
+
properties: {
|
|
633
|
+
scheduleId: { type: "string", minLength: 1 },
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
response: {
|
|
637
|
+
200: jsonObjectSchema,
|
|
638
|
+
400: errorResponseSchema,
|
|
639
|
+
404: errorResponseSchema,
|
|
640
|
+
},
|
|
641
|
+
},
|
|
642
|
+
}, async (request) => {
|
|
643
|
+
const params = request.params;
|
|
644
|
+
return service.pauseTimer(decodeURIComponent(params.scheduleId));
|
|
645
|
+
});
|
|
646
|
+
app.post("/timers/:scheduleId/resume", {
|
|
647
|
+
schema: {
|
|
648
|
+
tags: ["timers"],
|
|
649
|
+
params: {
|
|
650
|
+
type: "object",
|
|
651
|
+
required: ["scheduleId"],
|
|
652
|
+
properties: {
|
|
653
|
+
scheduleId: { type: "string", minLength: 1 },
|
|
654
|
+
},
|
|
655
|
+
},
|
|
656
|
+
response: {
|
|
657
|
+
200: jsonObjectSchema,
|
|
658
|
+
400: errorResponseSchema,
|
|
659
|
+
404: errorResponseSchema,
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
}, async (request) => {
|
|
663
|
+
const params = request.params;
|
|
664
|
+
return service.resumeTimer(decodeURIComponent(params.scheduleId));
|
|
665
|
+
});
|
|
666
|
+
app.delete("/timers/:scheduleId", {
|
|
667
|
+
schema: {
|
|
668
|
+
tags: ["timers"],
|
|
669
|
+
params: {
|
|
670
|
+
type: "object",
|
|
671
|
+
required: ["scheduleId"],
|
|
672
|
+
properties: {
|
|
673
|
+
scheduleId: { type: "string", minLength: 1 },
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
response: {
|
|
677
|
+
200: jsonObjectSchema,
|
|
678
|
+
404: errorResponseSchema,
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
}, async (request) => {
|
|
682
|
+
const params = request.params;
|
|
683
|
+
return service.removeTimer(decodeURIComponent(params.scheduleId));
|
|
684
|
+
});
|
|
566
685
|
app.get("/subscriptions", {
|
|
567
686
|
schema: {
|
|
568
687
|
tags: ["subscriptions"],
|
|
@@ -812,6 +931,39 @@ function buildFastifyServer(service) {
|
|
|
812
931
|
}),
|
|
813
932
|
};
|
|
814
933
|
});
|
|
934
|
+
app.post("/agents/:agentId/inbox/items", {
|
|
935
|
+
schema: {
|
|
936
|
+
tags: ["inbox"],
|
|
937
|
+
params: {
|
|
938
|
+
type: "object",
|
|
939
|
+
required: ["agentId"],
|
|
940
|
+
properties: {
|
|
941
|
+
agentId: { type: "string", minLength: 1 },
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
body: {
|
|
945
|
+
type: "object",
|
|
946
|
+
additionalProperties: false,
|
|
947
|
+
required: ["message"],
|
|
948
|
+
properties: {
|
|
949
|
+
message: { type: "string", minLength: 1 },
|
|
950
|
+
sender: { type: "string", minLength: 1 },
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
response: {
|
|
954
|
+
200: jsonObjectSchema,
|
|
955
|
+
400: errorResponseSchema,
|
|
956
|
+
404: errorResponseSchema,
|
|
957
|
+
},
|
|
958
|
+
},
|
|
959
|
+
}, async (request) => {
|
|
960
|
+
const params = request.params;
|
|
961
|
+
const body = request.body;
|
|
962
|
+
return service.addDirectInboxTextMessage(decodeURIComponent(params.agentId), {
|
|
963
|
+
message: body.message,
|
|
964
|
+
sender: body.sender ?? null,
|
|
965
|
+
});
|
|
966
|
+
});
|
|
815
967
|
app.get("/agents/:agentId/inbox/watch", {
|
|
816
968
|
schema: {
|
|
817
969
|
tags: ["inbox"],
|
|
@@ -1060,7 +1212,11 @@ function normalizeValidationMessage(message) {
|
|
|
1060
1212
|
return message;
|
|
1061
1213
|
}
|
|
1062
1214
|
function isBadRequestError(message) {
|
|
1063
|
-
return (message.startsWith("
|
|
1215
|
+
return (message.startsWith("direct inbox ") ||
|
|
1216
|
+
message.startsWith("timers require ") ||
|
|
1217
|
+
message.startsWith("timer ") ||
|
|
1218
|
+
message.startsWith("invalid timezone: ") ||
|
|
1219
|
+
message.startsWith("manual append is not supported") ||
|
|
1064
1220
|
message.startsWith("sources/events requires") ||
|
|
1065
1221
|
message.startsWith("source remove requires") ||
|
|
1066
1222
|
message.startsWith("deliveryHandle requires") ||
|