@robbeverhelst/do 1.164.0 → 1.165.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.
Files changed (2) hide show
  1. package/dist/do.js +365 -11
  2. package/package.json +1 -1
package/dist/do.js CHANGED
@@ -969,7 +969,8 @@ var GLOBAL_BOOLEAN_FLAGS = [
969
969
  "help",
970
970
  "version",
971
971
  "allow-destructive",
972
- "dry-run"
972
+ "dry-run",
973
+ "draft"
973
974
  ];
974
975
  var SHORT_ALIASES = {
975
976
  h: "help",
@@ -19983,6 +19984,186 @@ var agentFilingSchema = object({
19983
19984
  date: string2().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()
19984
19985
  });
19985
19986
 
19987
+ // ../../packages/schema/src/contracts/agent-gateway.ts
19988
+ var AGENT_GATEWAY_VERSION = 1;
19989
+ var AGENT_GATEWAY_MAX_FRAME_BYTES = 1024 * 1024;
19990
+ var threadSlugSchema = string2().regex(/^[a-z0-9][a-z0-9-]{0,39}$/, "a thread slug is lower-case, like `build`");
19991
+ var postLevelSchema = _enum2(["needs_you", "told", "for_later", "silent"]);
19992
+ var postKindSchema = _enum2(["message", "brief", "card", "run"]);
19993
+ var runStatusSchema = _enum2(["running", "needs_you", "done", "failed", "stopped"]);
19994
+ var externalIdSchema = string2().min(1).max(200).regex(/^[\x21-\x7e]+$/, "printable ASCII without spaces");
19995
+ var AGENT_POST_TEXT_MAX = 64000;
19996
+ var postTextSchema = string2().max(AGENT_POST_TEXT_MAX);
19997
+ var gatewayMemberSchema = object({
19998
+ id: string2().min(1),
19999
+ name: string2(),
20000
+ role: baseRoleSchema
20001
+ });
20002
+ var gatewayThreadSchema = object({
20003
+ id: ulidSchema,
20004
+ memberId: string2().min(1),
20005
+ slug: threadSlugSchema.nullable(),
20006
+ title: string2().nullable(),
20007
+ standing: boolean3(),
20008
+ deleted: boolean3().optional()
20009
+ });
20010
+ var gatewayAttachmentSchema = object({
20011
+ id: ulidSchema,
20012
+ kind: _enum2(["image", "voice", "audio", "video", "file"]),
20013
+ mime: string2().min(1),
20014
+ name: string2(),
20015
+ size: number2().int().nonnegative()
20016
+ });
20017
+ var inboundMessageSchema = object({
20018
+ id: ulidSchema,
20019
+ threadId: ulidSchema,
20020
+ member: object({ id: string2().min(1), name: string2() }),
20021
+ text: string2(),
20022
+ createdAt: string2().datetime(),
20023
+ attachments: array(gatewayAttachmentSchema).default([]),
20024
+ replyToPostId: ulidSchema.optional(),
20025
+ context: agentPageContextSchema.optional(),
20026
+ history: array(object({ role: _enum2(["member", "agent"]), text: string2() })).optional()
20027
+ });
20028
+ var threadTargetSchema = union([
20029
+ object({ id: ulidSchema }).strict(),
20030
+ object({ slug: threadSlugSchema, memberId: string2().min(1).optional() }).strict()
20031
+ ]);
20032
+ var postRunSchema = object({ status: runStatusSchema });
20033
+ var agentPostInputSchema = object({
20034
+ externalId: externalIdSchema,
20035
+ thread: threadTargetSchema,
20036
+ text: postTextSchema,
20037
+ kind: postKindSchema.default("message"),
20038
+ level: postLevelSchema.nullish(),
20039
+ replyToMessageId: ulidSchema.optional(),
20040
+ attachments: array(ulidSchema).max(20).default([]),
20041
+ final: boolean3().default(true),
20042
+ run: postRunSchema.optional(),
20043
+ recovered: boolean3().optional()
20044
+ });
20045
+ var agentPostEditSchema = object({
20046
+ text: postTextSchema.optional(),
20047
+ final: boolean3().optional(),
20048
+ level: postLevelSchema.optional(),
20049
+ run: postRunSchema.optional()
20050
+ }).refine((e) => Object.values(e).some((v) => v !== undefined), {
20051
+ message: "send text, final, level or run"
20052
+ });
20053
+ var agentPostResultSchema = object({ postId: ulidSchema, created: boolean3() });
20054
+ var promptOptionSchema = object({
20055
+ id: string2().min(1).max(40),
20056
+ label: string2().min(1).max(80),
20057
+ style: _enum2(["primary", "danger", ""]).default("")
20058
+ });
20059
+ var agentPromptSchema = object({
20060
+ promptId: string2().min(1).max(100),
20061
+ threadId: ulidSchema,
20062
+ kind: _enum2(["exec_approval", "clarify", "slash_confirm"]),
20063
+ text: postTextSchema,
20064
+ options: array(promptOptionSchema).min(1).max(12),
20065
+ allowText: boolean3().default(false),
20066
+ expiresAt: string2().datetime(),
20067
+ command: string2().optional(),
20068
+ description: string2().optional()
20069
+ });
20070
+ var agentGatewayErrorCodeSchema = _enum2([
20071
+ "bad_request",
20072
+ "not_found",
20073
+ "thread_not_found",
20074
+ "not_member",
20075
+ "too_large",
20076
+ "rate_limited",
20077
+ "unsupported",
20078
+ "internal"
20079
+ ]);
20080
+ var agentGatewayErrorSchema = object({
20081
+ code: agentGatewayErrorCodeSchema,
20082
+ message: string2(),
20083
+ retryable: boolean3()
20084
+ });
20085
+ var frame = (type, shape) => object({
20086
+ v: literal(AGENT_GATEWAY_VERSION),
20087
+ type: literal(type),
20088
+ id: string2().min(1).max(64),
20089
+ ...shape
20090
+ });
20091
+ var readyFrameSchema = frame("ready", {
20092
+ space: object({
20093
+ id: string2().min(1),
20094
+ name: string2(),
20095
+ ownerMemberId: string2().min(1)
20096
+ }),
20097
+ agent: object({
20098
+ id: string2().min(1),
20099
+ name: string2(),
20100
+ homeSlug: threadSlugSchema
20101
+ }),
20102
+ members: array(gatewayMemberSchema),
20103
+ threads: array(gatewayThreadSchema),
20104
+ heartbeatSec: number2().int().positive()
20105
+ });
20106
+ var membersFrameSchema = frame("members", { members: array(gatewayMemberSchema) });
20107
+ var threadsFrameSchema = frame("threads", { threads: array(gatewayThreadSchema) });
20108
+ var messageFrameSchema = frame("message", { message: inboundMessageSchema });
20109
+ var promptAnswerFrameSchema = frame("prompt.answer", {
20110
+ promptId: string2().min(1),
20111
+ optionId: string2().min(1),
20112
+ text: string2().optional(),
20113
+ memberId: string2().min(1),
20114
+ threadId: ulidSchema
20115
+ });
20116
+ var interruptFrameSchema = frame("interrupt", {
20117
+ threadId: ulidSchema,
20118
+ memberId: string2().min(1)
20119
+ });
20120
+ var ackFrameSchema = frame("ack", {
20121
+ ref: string2().min(1),
20122
+ ok: boolean3(),
20123
+ postId: ulidSchema.optional(),
20124
+ created: boolean3().optional(),
20125
+ error: agentGatewayErrorSchema.optional()
20126
+ });
20127
+ var agentGatewayServerFrameSchema = discriminatedUnion("type", [
20128
+ readyFrameSchema,
20129
+ membersFrameSchema,
20130
+ threadsFrameSchema,
20131
+ messageFrameSchema,
20132
+ promptAnswerFrameSchema,
20133
+ interruptFrameSchema,
20134
+ ackFrameSchema
20135
+ ]);
20136
+ var messageAckFrameSchema = frame("message.ack", { messageId: ulidSchema });
20137
+ var postFrameSchema = frame("post", { post: agentPostInputSchema });
20138
+ var postEditFrameSchema = frame("post.edit", {
20139
+ postId: ulidSchema,
20140
+ edit: agentPostEditSchema
20141
+ });
20142
+ var postDeleteFrameSchema = frame("post.delete", { postId: ulidSchema });
20143
+ var typingFrameSchema = frame("typing", { threadId: ulidSchema, on: boolean3() });
20144
+ var promptFrameSchema = frame("prompt", { prompt: agentPromptSchema });
20145
+ var promptCloseFrameSchema = frame("prompt.close", {
20146
+ promptId: string2().min(1),
20147
+ optionId: string2().optional(),
20148
+ expired: boolean3()
20149
+ });
20150
+ var agentGatewayClientFrameSchema = discriminatedUnion("type", [
20151
+ messageAckFrameSchema,
20152
+ postFrameSchema,
20153
+ postEditFrameSchema,
20154
+ postDeleteFrameSchema,
20155
+ typingFrameSchema,
20156
+ promptFrameSchema,
20157
+ promptCloseFrameSchema
20158
+ ]);
20159
+ var SERVER_FRAME_TYPES = new Set(agentGatewayServerFrameSchema.options.map((o) => o.shape.type.value));
20160
+ var CLIENT_FRAME_TYPES = new Set(agentGatewayClientFrameSchema.options.map((o) => o.shape.type.value));
20161
+ var agentThreadsResponseSchema = object({ threads: array(gatewayThreadSchema) });
20162
+ function homeSlugFor(agentName) {
20163
+ const slug = agentName.normalize("NFKD").replace(/[\u0300-\u036F]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/, "");
20164
+ return slug || "agent";
20165
+ }
20166
+
19986
20167
  // ../../packages/schema/src/contracts/roles.ts
19987
20168
  var permissionSetSchema = createSelectSchema(permissionSets, {
19988
20169
  createdAt: exports_coerce.date(),
@@ -21083,7 +21264,7 @@ function parseCapture2(input, now) {
21083
21264
 
21084
21265
  // src/meta.ts
21085
21266
  var CLI_NAME = "do";
21086
- var VERSION = "1.164.0";
21267
+ var VERSION = "1.165.0";
21087
21268
  var API_PREFIX = "/api/v1";
21088
21269
  var DEFAULT_API_URL = "http://localhost:3001";
21089
21270
 
@@ -22357,6 +22538,95 @@ var COMMAND_SPECS = [
22357
22538
  returns: "None",
22358
22539
  notes: "Owner/admin only. An Agent's key gets a 428 confirmation_required until the member confirms in the Echo panel."
22359
22540
  },
22541
+ {
22542
+ path: ["agent", "post"],
22543
+ summary: "Post into a member's thread as the Space's agent (or --edit / --delete a post).",
22544
+ args: [
22545
+ { name: "text", required: false, description: "The text (markdown), or - to read stdin." }
22546
+ ],
22547
+ flags: [
22548
+ {
22549
+ name: "thread",
22550
+ type: "string",
22551
+ description: "The thread: its slug (`build`) or id. Default: the agent's home thread."
22552
+ },
22553
+ {
22554
+ name: "member",
22555
+ type: "string",
22556
+ description: "With a slug: the member whose thread it is (default: the Space's first owner)."
22557
+ },
22558
+ { name: "kind", type: "string", description: "message (default), brief, card or run." },
22559
+ {
22560
+ name: "level",
22561
+ type: "string",
22562
+ description: "needs_you, told, for_later or silent (default by kind: a message told, the rest for_later)."
22563
+ },
22564
+ {
22565
+ name: "external-id",
22566
+ type: "string",
22567
+ description: "Your idempotency key (`card:repos`): the same key again returns the post, and a card is replaced in place. Default: a fresh one."
22568
+ },
22569
+ {
22570
+ name: "draft",
22571
+ type: "boolean",
22572
+ description: "Not final yet (a reply still being edited)."
22573
+ },
22574
+ { name: "edit", type: "string", description: "Edit this post (its id) instead: its text." },
22575
+ { name: "delete", type: "string", description: "Take this post (its id) back." }
22576
+ ],
22577
+ scopes: ["agents:converse"],
22578
+ returns: "AgentPostResult",
22579
+ notes: "Only the Space's agent's key. An unknown slug is thread_not_found (the member makes standing threads); the home slug always works. --edit and --delete act on the agent's own posts."
22580
+ },
22581
+ {
22582
+ path: ["agent", "threads"],
22583
+ summary: "List every member's standing threads (the ones a slug addresses).",
22584
+ args: [],
22585
+ flags: [],
22586
+ scopes: ["agents:converse"],
22587
+ returns: "{ threads: GatewayThread[] }",
22588
+ notes: "Ad-hoc threads aren't listed."
22589
+ },
22590
+ {
22591
+ path: ["echo", "post"],
22592
+ summary: "Same as `agent post`: post into a member's thread as the Space's agent.",
22593
+ args: [
22594
+ { name: "text", required: false, description: "The text (markdown), or - to read stdin." }
22595
+ ],
22596
+ flags: [
22597
+ {
22598
+ name: "thread",
22599
+ type: "string",
22600
+ description: "The thread: its slug (`build`) or id. Default: the agent's home thread."
22601
+ },
22602
+ {
22603
+ name: "member",
22604
+ type: "string",
22605
+ description: "With a slug: the member whose thread it is (default: the Space's first owner)."
22606
+ },
22607
+ { name: "kind", type: "string", description: "message (default), brief, card or run." },
22608
+ {
22609
+ name: "level",
22610
+ type: "string",
22611
+ description: "needs_you, told, for_later or silent (default by kind: a message told, the rest for_later)."
22612
+ },
22613
+ {
22614
+ name: "external-id",
22615
+ type: "string",
22616
+ description: "Your idempotency key (`card:repos`): the same key again returns the post, and a card is replaced in place. Default: a fresh one."
22617
+ },
22618
+ {
22619
+ name: "draft",
22620
+ type: "boolean",
22621
+ description: "Not final yet (a reply still being edited)."
22622
+ },
22623
+ { name: "edit", type: "string", description: "Edit this post (its id) instead: its text." },
22624
+ { name: "delete", type: "string", description: "Take this post (its id) back." }
22625
+ ],
22626
+ scopes: ["agents:converse"],
22627
+ returns: "AgentPostResult",
22628
+ notes: "Only the Space's agent's key. An unknown slug is thread_not_found (the member makes standing threads); the home slug always works. --edit and --delete act on the agent's own posts."
22629
+ },
22360
22630
  {
22361
22631
  path: ["echo", "threads", "list"],
22362
22632
  summary: "List your recent Echo threads, newest first.",
@@ -23718,17 +23988,36 @@ function renderEchoThread(data, mode) {
23718
23988
  return paint(mode, "dim", "No Echo thread yet.");
23719
23989
  const head = `${thread.title ?? "New conversation"} ${paint(mode, "dim", thread.id ?? "")}`;
23720
23990
  const turns = thread.turns ?? [];
23721
- if (turns.length === 0)
23991
+ const posts = thread.posts ?? [];
23992
+ if (turns.length === 0 && posts.length === 0) {
23722
23993
  return `${head}
23723
23994
  ${paint(mode, "dim", "(nothing asked yet)")}`;
23724
- const lines = turns.flatMap((t) => {
23725
- const note = t.status === "error" ? ` (failed: ${t.error ?? "error"})` : t.status === "cancelled" ? " (stopped)" : t.status === "streaming" ? " (still answering)" : "";
23726
- return [
23727
- ` ${paint(mode, "bold", "You")} ${t.question ?? ""}`,
23728
- ` ${paint(mode, "bold", "Echo")} ${t.reply ?? ""}${paint(mode, "dim", note)}`
23729
- ];
23730
- });
23731
- return [head, ...lines].join(`
23995
+ }
23996
+ const entries = [
23997
+ ...turns.map((t, i) => {
23998
+ const note = t.status === "error" ? ` (failed: ${t.error ?? "error"})` : t.status === "cancelled" ? " (stopped)" : t.status === "streaming" ? " (still answering)" : "";
23999
+ return {
24000
+ at: t.createdAt ?? "",
24001
+ order: i,
24002
+ lines: [
24003
+ ` ${paint(mode, "bold", "You")} ${t.question ?? ""}`,
24004
+ ` ${paint(mode, "bold", "Echo")} ${t.reply ?? ""}${paint(mode, "dim", note)}`
24005
+ ]
24006
+ };
24007
+ }),
24008
+ ...posts.map((p, i) => {
24009
+ const who = p.author === "member" ? "You" : p.agentName ?? "Agent";
24010
+ const note = [p.kind && p.kind !== "message" ? p.kind : "", p.final === false ? "draft" : ""].filter(Boolean).join(", ");
24011
+ return {
24012
+ at: p.createdAt ?? "",
24013
+ order: turns.length + i,
24014
+ lines: [
24015
+ ` ${paint(mode, "bold", who)} ${p.text ?? ""}${note ? paint(mode, "dim", ` (${note})`) : ""}`
24016
+ ]
24017
+ };
24018
+ })
24019
+ ].sort((a, b) => a.at === b.at ? a.order - b.order : a.at < b.at ? -1 : 1);
24020
+ return [head, ...entries.flatMap((e) => e.lines)].join(`
23732
24021
  `);
23733
24022
  }
23734
24023
  function actorText(actor) {
@@ -24655,6 +24944,12 @@ var HANDLERS = {
24655
24944
  await requireClient().delete("/agent/connection");
24656
24945
  return { data: { ok: true, removed: "agent connection" }, render: renderRecord };
24657
24946
  },
24947
+ "agent post": agentPost,
24948
+ "echo post": agentPost,
24949
+ "agent threads": async ({ requireClient }) => ({
24950
+ data: await requireClient().get("/agent/threads"),
24951
+ render: renderRecord
24952
+ }),
24658
24953
  "echo threads list": async ({ flags, requireClient }) => ({
24659
24954
  data: await requireClient().getPage("/echo/threads", {
24660
24955
  query: { limit: flagString(flags, "limit") }
@@ -25171,6 +25466,65 @@ async function readAgentKey(io, flags) {
25171
25466
  const fromEnv = io.env[AGENT_KEY_ENV]?.trim();
25172
25467
  return fromEnv ? fromEnv : undefined;
25173
25468
  }
25469
+ async function agentPost({ io, args, flags, requireClient }) {
25470
+ const client = requireClient();
25471
+ const remove = flagString(flags, "delete");
25472
+ if (remove !== undefined) {
25473
+ await client.delete(`/agent/posts/${encodeURIComponent(remove)}`);
25474
+ return { data: { ok: true, deleted: remove }, render: renderRecord };
25475
+ }
25476
+ const text = await postText(io, args);
25477
+ const edit = flagString(flags, "edit");
25478
+ if (edit !== undefined) {
25479
+ const body = parseInput(agentPostEditSchema, {
25480
+ text,
25481
+ ...flagBool(flags, "draft") ? { final: false } : {},
25482
+ level: flagString(flags, "level")
25483
+ });
25484
+ return {
25485
+ data: await client.patch(`/agent/posts/${encodeURIComponent(edit)}`, body),
25486
+ render: renderRecord
25487
+ };
25488
+ }
25489
+ const thread = flagString(flags, "thread") ?? await homeSlug(client);
25490
+ const member = flagString(flags, "member");
25491
+ const body = parseInput(agentPostInputSchema, {
25492
+ externalId: flagString(flags, "external-id") ?? `cli:${ulid3()}`,
25493
+ thread: ULID.test(thread) ? { id: thread.toUpperCase() } : { slug: thread, ...member ? { memberId: member } : {} },
25494
+ text,
25495
+ kind: flagString(flags, "kind"),
25496
+ level: flagString(flags, "level"),
25497
+ final: !flagBool(flags, "draft")
25498
+ });
25499
+ return { data: await client.post("/agent/posts", body), render: renderRecord };
25500
+ }
25501
+ async function postText(io, args) {
25502
+ if (args.length === 1 && args[0] === "-") {
25503
+ const lines = [];
25504
+ if (io.stdin)
25505
+ for await (const line of io.stdin)
25506
+ lines.push(line);
25507
+ const text = lines.join(`
25508
+ `).trim();
25509
+ if (!text)
25510
+ throw usageError("read no text from stdin");
25511
+ return text;
25512
+ }
25513
+ const text = args.join(" ").trim();
25514
+ if (!text)
25515
+ throw usageError("agent post needs the text (or - to read it from stdin)");
25516
+ return text;
25517
+ }
25518
+ async function homeSlug(client) {
25519
+ const me = await client.get("/whoami");
25520
+ if (!me.agentId)
25521
+ throw usageError("this key isn't an Agent's: pass --thread <slug|id>");
25522
+ const { data } = await client.getPage("/agents");
25523
+ const agent = data.find((a) => a.id === me.agentId);
25524
+ if (!agent)
25525
+ throw usageError("couldn't read the agent's name: pass --thread <slug|id>");
25526
+ return homeSlugFor(agent.name);
25527
+ }
25174
25528
  var ULID = /^[0-9A-HJKMNP-TV-Z]{26}$/i;
25175
25529
  async function resolveAgentId(client, agent) {
25176
25530
  if (ULID.test(agent))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robbeverhelst/do",
3
- "version": "1.164.0",
3
+ "version": "1.165.0",
4
4
  "private": false,
5
5
  "description": "do — the agent-drivable CLI over the public /api/v1 surface",
6
6
  "homepage": "https://github.com/robbeverhelst/do/tree/main/apps/cli",