neko-harness 0.4.3 → 0.4.4

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.
@@ -9,8 +9,8 @@
9
9
  href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='26' fill='%23ffb7c5'/%3E%3Ctext x='50' y='70' font-size='58' text-anchor='middle' fill='%23ffffff' font-family='sans-serif' font-weight='bold'%3En%3C/text%3E%3C/svg%3E"
10
10
  />
11
11
  <title>neko harness</title>
12
- <script type="module" crossorigin src="/assets/index-DLyLR2Bn.js"></script>
13
- <link rel="stylesheet" crossorigin href="/assets/index-CAwqBiZ6.css">
12
+ <script type="module" crossorigin src="/assets/index-BqLuRtzq.js"></script>
13
+ <link rel="stylesheet" crossorigin href="/assets/index-BjLs6qQU.css">
14
14
  </head>
15
15
  <body>
16
16
  <div id="root"></div>
package/dist/gateway.js CHANGED
@@ -17256,6 +17256,50 @@ function savedDefaultImagePrompt(content, value) {
17256
17256
  import { defineEntry } from "@earendil-works/pi-durable";
17257
17257
  var NekoInput = defineEntry("app.neko.input");
17258
17258
 
17259
+ // packages/core/src/queued.ts
17260
+ var QUEUED_PREVIEW_CHARS = 300;
17261
+ var textOf = (content) => {
17262
+ if (typeof content === "string")
17263
+ return content;
17264
+ if (!Array.isArray(content))
17265
+ return "";
17266
+ return content.flatMap((b) => b && b.type === "text" && typeof b.text === "string" ? [b.text] : []).join("");
17267
+ };
17268
+ var clip = (text) => {
17269
+ const chars = [...text.replace(/\s+/g, " ").trim()];
17270
+ return chars.length > QUEUED_PREVIEW_CHARS ? `${chars.slice(0, QUEUED_PREVIEW_CHARS - 1).join("")}\u2026` : chars.join("");
17271
+ };
17272
+ function queuedUserInput(raw) {
17273
+ const item = raw;
17274
+ if (item.mode !== "steer" && item.mode !== "followUp")
17275
+ return;
17276
+ if (item.entry?.kind !== NekoInput.kind)
17277
+ return;
17278
+ const data = item.entry.data;
17279
+ if (!data || data.jobReport || data.subagentRoundId)
17280
+ return;
17281
+ const defaultPrompt = Array.isArray(item.content) ? savedDefaultImagePrompt(item.content, data.defaultImagePrompt) : undefined;
17282
+ const content = defaultPrompt && Array.isArray(item.content) ? item.content.slice(1) : item.content;
17283
+ const skill = data.skill ? { name: data.skill.name, args: data.skill.args } : undefined;
17284
+ return {
17285
+ id: item.id,
17286
+ mode: item.mode,
17287
+ text: skill ? `/skill:${skill.name}${skill.args ? ` ${skill.args}` : ""}` : textOf(content),
17288
+ images: data.images?.length ?? 0,
17289
+ ...skill ? { skill } : {}
17290
+ };
17291
+ }
17292
+ function projectQueued(view) {
17293
+ const items = view.docs["pi.inbox"]?.items ?? [];
17294
+ return items.flatMap((raw) => {
17295
+ const input = queuedUserInput(raw);
17296
+ if (!input)
17297
+ return [];
17298
+ const { text, ...rest } = input;
17299
+ return [{ ...rest, preview: clip(text) }];
17300
+ });
17301
+ }
17302
+
17259
17303
  // packages/core/src/workspace.ts
17260
17304
  import { accessSync, constants, realpathSync, statSync } from "fs";
17261
17305
  import { isAbsolute, resolve as resolve2 } from "path";
@@ -17576,6 +17620,7 @@ function projectChatView(view, modelLabel, options = {}) {
17576
17620
  },
17577
17621
  cwd: savedWorkspace(agent?.cwd),
17578
17622
  busy: live.run !== undefined,
17623
+ queued: projectQueued(view),
17579
17624
  status,
17580
17625
  usage: {
17581
17626
  input: usage.input,
@@ -17721,6 +17766,7 @@ import {
17721
17766
  createRegistry,
17722
17767
  defineDoc as defineDoc5,
17723
17768
  Harness,
17769
+ InboxDoc as InboxDoc2,
17724
17770
  LiveDoc as importLiveDoc,
17725
17771
  MemoryStorage,
17726
17772
  UsageDoc as UsageDoc2,
@@ -19415,6 +19461,14 @@ var createResourceSnapshots = (load, submission) => {
19415
19461
  discard(id, requestId) {
19416
19462
  conversations.get(id)?.pending.delete(requestId);
19417
19463
  },
19464
+ move(id, from, to) {
19465
+ const pending = conversations.get(id)?.pending;
19466
+ const snapshot = pending?.get(from);
19467
+ if (!pending || !snapshot)
19468
+ return;
19469
+ pending.delete(from);
19470
+ pending.set(to, snapshot);
19471
+ },
19418
19472
  forget(id) {
19419
19473
  conversations.delete(id);
19420
19474
  }
@@ -21065,6 +21119,57 @@ async function createNekoRuntime(options = {}) {
21065
21119
  },
21066
21120
  abort() {
21067
21121
  return target.abort(context);
21122
+ },
21123
+ async withdraw(submissionId) {
21124
+ const id = submissionId;
21125
+ const inbox = await harness.snapshot(InboxDoc2, target.id, context);
21126
+ const item = inbox?.items.find((i) => i.id === id);
21127
+ const input = item ? queuedUserInput(item) : undefined;
21128
+ if (item && !input)
21129
+ return { result: "unknown" };
21130
+ const requestId = (await (await harness.submission(id, context))?.status(context))?.requestId;
21131
+ const outcome = await harness.abortSubmission(id, context, target.id);
21132
+ if (outcome !== "aborted") {
21133
+ return { result: outcome === "already_placed" ? "placed" : outcome === "settled" ? "settled" : "unknown" };
21134
+ }
21135
+ if (requestId !== undefined)
21136
+ resourceSnapshots.discard(target.id, requestId);
21137
+ await metadata.reconcile(target.id);
21138
+ return { result: "withdrawn", ...input ? { text: input.text, images: input.images } : {} };
21139
+ },
21140
+ async steer(submissionId) {
21141
+ const id = submissionId;
21142
+ const inbox = await harness.snapshot(InboxDoc2, target.id, context);
21143
+ const item = inbox?.items.find((i) => i.id === id);
21144
+ const input = item ? queuedUserInput(item) : undefined;
21145
+ if (item && (!input || item.mode === "write"))
21146
+ return { result: "unknown" };
21147
+ if (item && input?.mode === "steer")
21148
+ return { result: "steered", id: submissionId };
21149
+ const requestId = (await (await harness.submission(id, context))?.status(context))?.requestId;
21150
+ const outcome = await harness.abortSubmission(id, context, target.id);
21151
+ if (outcome !== "aborted" || !item || !input) {
21152
+ return { result: outcome === "already_placed" ? "placed" : outcome === "settled" ? "settled" : "unknown" };
21153
+ }
21154
+ const steerRequestId = requestId !== undefined ? `steer:${requestId}` : crypto.randomUUID();
21155
+ try {
21156
+ const next = await target.submit({
21157
+ type: "input",
21158
+ content: item.content,
21159
+ ...item.entry ? { entry: item.entry } : {},
21160
+ requestId: steerRequestId,
21161
+ whenBusy: "steer"
21162
+ }, context);
21163
+ if (requestId !== undefined)
21164
+ resourceSnapshots.move(target.id, requestId, steerRequestId);
21165
+ await metadata.reconcile(target.id);
21166
+ return { result: "steered", id: next.id };
21167
+ } catch {
21168
+ if (requestId !== undefined)
21169
+ resourceSnapshots.discard(target.id, requestId);
21170
+ await metadata.reconcile(target.id);
21171
+ return { result: "withdrawn", text: input.text, images: input.images };
21172
+ }
21068
21173
  }
21069
21174
  });
21070
21175
  const rootSession = makeSession(conversation);
@@ -21477,10 +21582,29 @@ var JobOutputSchema = Type5.Object({
21477
21582
  truncated: bool,
21478
21583
  dropped: integer
21479
21584
  });
21585
+ var QueuedInputSchema = Type5.Object({
21586
+ id: integer,
21587
+ mode: Type5.Enum(["steer", "followUp"]),
21588
+ preview: str,
21589
+ images: integer,
21590
+ skill: Type5.Optional(Type5.Object({ name: str, args: str }))
21591
+ });
21592
+ var WithdrawResultSchema = Type5.Object({
21593
+ result: Type5.Enum(["withdrawn", "placed", "settled", "unknown"]),
21594
+ text: Type5.Optional(str),
21595
+ images: Type5.Optional(integer)
21596
+ });
21597
+ var SteerResultSchema = Type5.Object({
21598
+ result: Type5.Enum(["steered", "withdrawn", "placed", "settled", "unknown"]),
21599
+ id: Type5.Optional(integer),
21600
+ text: Type5.Optional(str),
21601
+ images: Type5.Optional(integer)
21602
+ });
21480
21603
  var ChatViewSchema = Type5.Object({
21481
21604
  historyBefore: Type5.Optional(Type5.Union([integer, nil])),
21482
21605
  agents: Type5.Optional(Type5.Array(SubagentSummarySchema)),
21483
21606
  jobs: Type5.Optional(Type5.Array(JobSummarySchema)),
21607
+ queued: Type5.Optional(Type5.Array(QueuedInputSchema)),
21484
21608
  rounds: Type5.Optional(Type5.Array(DelegationSchema)),
21485
21609
  subagents: Type5.Optional(Type5.Array(DelegationSchema)),
21486
21610
  taskUsage: Type5.Optional(usage),
@@ -21685,6 +21809,8 @@ var NekoChat = contract("neko.chat", {
21685
21809
  whenBusy: Type5.Optional(Type5.Enum(["steer", "followUp"]))
21686
21810
  }, { additionalProperties: false }), nil),
21687
21811
  abort: rpc(nil, nil),
21812
+ withdraw: rpc(Type5.Object({ id: integer }, { additionalProperties: false }), WithdrawResultSchema),
21813
+ steer: rpc(Type5.Object({ id: integer }, { additionalProperties: false }), SteerResultSchema),
21688
21814
  usage: rpc(nil, usage),
21689
21815
  skills: rpc(nil, Type5.Array(Type5.Object({ name: str, description: str, location: str, level }))),
21690
21816
  jobOutput: rpc(Type5.Object({ jobId: str, since: Type5.Optional(integer) }, { additionalProperties: false }), JobOutputSchema),
@@ -22586,7 +22712,7 @@ function subagentClient(connect) {
22586
22712
  import { createHash as createHash2 } from "crypto";
22587
22713
 
22588
22714
  // packages/core/src/application-version.ts
22589
- var RUNNING_VERSION = "0.4.3";
22715
+ var RUNNING_VERSION = "0.4.4";
22590
22716
  var APPLICATION_PROTOCOL = 6;
22591
22717
  function stableVersion(value) {
22592
22718
  return typeof value === "string" && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.test(value) && value.split(".").every((n) => Number.isSafeInteger(Number(n)));
@@ -22765,8 +22891,15 @@ var MobileAgentSchema = Type7.Object({
22765
22891
  roundCount: int,
22766
22892
  approvalCount: int
22767
22893
  });
22894
+ var MobileQueuedSchema = Type7.Object({
22895
+ id: int,
22896
+ mode: Type7.Union([Type7.Literal("steer"), Type7.Literal("followUp")]),
22897
+ preview: Type7.String(),
22898
+ images: int
22899
+ });
22768
22900
  var MobileChatStatusSchema = Type7.Object({
22769
22901
  busy: Type7.Boolean(),
22902
+ queued: opt(Type7.Array(MobileQueuedSchema)),
22770
22903
  status: nullable2(str2),
22771
22904
  usage: MobileUsageSchema,
22772
22905
  taskUsage: opt(MobileUsageSchema),
@@ -22850,6 +22983,34 @@ var MOBILE_API_V1_METHODS = {
22850
22983
  params: Type7.Object({ ...subscription, requestId: Type7.String({ minLength: 1, maxLength: 128 }) }),
22851
22984
  result: Type7.Object({ status: Type7.Union([Type7.Literal("accepted"), Type7.Literal("unknown")]) })
22852
22985
  },
22986
+ "chat.steer": {
22987
+ params: Type7.Object({ ...subscription, id: int }),
22988
+ result: Type7.Object({
22989
+ result: Type7.Union([
22990
+ Type7.Literal("steered"),
22991
+ Type7.Literal("withdrawn"),
22992
+ Type7.Literal("placed"),
22993
+ Type7.Literal("settled"),
22994
+ Type7.Literal("unknown")
22995
+ ]),
22996
+ id: opt(int),
22997
+ text: opt(Type7.String()),
22998
+ images: opt(int)
22999
+ })
23000
+ },
23001
+ "chat.withdraw": {
23002
+ params: Type7.Object({ ...subscription, id: int }),
23003
+ result: Type7.Object({
23004
+ result: Type7.Union([
23005
+ Type7.Literal("withdrawn"),
23006
+ Type7.Literal("placed"),
23007
+ Type7.Literal("settled"),
23008
+ Type7.Literal("unknown")
23009
+ ]),
23010
+ text: opt(Type7.String()),
23011
+ images: opt(int)
23012
+ })
23013
+ },
22853
23014
  "chat.abort": { params: Type7.Object(subscription), result: Type7.Object({ accepted: Type7.Boolean() }) },
22854
23015
  "subagents.open": {
22855
23016
  params: Type7.Object({ ...subscription, agentId: opt(Type7.String({ minLength: 1 })), taskId: opt(int) }),
@@ -23137,6 +23298,9 @@ function mapStatus(view, options = {}) {
23137
23298
  const latest = view.compaction?.latest;
23138
23299
  return {
23139
23300
  busy: view.busy,
23301
+ ...view.queued === undefined ? {} : {
23302
+ queued: view.queued.map(({ id, mode, preview, images }) => ({ id, mode, preview, images }))
23303
+ },
23140
23304
  status: view.status,
23141
23305
  usage: mapUsage(view.usage),
23142
23306
  ...view.taskUsage === undefined ? {} : { taskUsage: mapUsage(view.taskUsage) },
@@ -23623,6 +23787,10 @@ class MobileAdapter {
23623
23787
  return this.chatSubmit(params);
23624
23788
  case "chat.submissionStatus":
23625
23789
  return this.chatSubmissionStatus(params);
23790
+ case "chat.steer":
23791
+ return this.chatSteer(params);
23792
+ case "chat.withdraw":
23793
+ return this.chatWithdraw(params);
23626
23794
  case "chat.abort":
23627
23795
  return this.chatAbort(params);
23628
23796
  case "subagents.open":
@@ -23852,6 +24020,16 @@ class MobileAdapter {
23852
24020
  const accepted = await this.runtime.submissionAccepted(state.sessionId, params.requestId);
23853
24021
  return { status: accepted ? "accepted" : "unknown" };
23854
24022
  }
24023
+ async chatSteer(params) {
24024
+ const state = this.requireChat(params);
24025
+ this.assertAuthorized();
24026
+ return remote2(() => state.chat.steer({ id: params.id }), "rejected");
24027
+ }
24028
+ async chatWithdraw(params) {
24029
+ const state = this.requireChat(params);
24030
+ this.assertAuthorized();
24031
+ return remote2(() => state.chat.withdraw({ id: params.id }), "rejected");
24032
+ }
23855
24033
  async chatAbort(params) {
23856
24034
  const state = this.requireChat(params);
23857
24035
  this.assertAuthorized();
@@ -26109,6 +26287,7 @@ var emptyView = {
26109
26287
  agent: { model: null, thinkingLevel: null },
26110
26288
  cwd: "",
26111
26289
  busy: false,
26290
+ queued: [],
26112
26291
  status: null,
26113
26292
  usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 },
26114
26293
  items: [],
@@ -26376,6 +26555,8 @@ function serveSessions(host, runtime, context) {
26376
26555
  await projection.value.session.abort();
26377
26556
  return null;
26378
26557
  },
26558
+ withdraw: ({ id }) => projection.value.session.withdraw(id),
26559
+ steer: ({ id }) => projection.value.session.steer(id),
26379
26560
  skills: () => runtime.listSkills(projection.value.session.id),
26380
26561
  jobOutput: async ({ jobId, since }) => {
26381
26562
  try {