@songsid/agend 2.2.0-beta.6 → 2.2.0-beta.7
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/agent-cli-instructions.md +6 -2
- package/dist/agent-cli.js +9 -5
- package/dist/agent-cli.js.map +1 -1
- package/dist/backend/muse.d.ts +26 -0
- package/dist/backend/muse.js +131 -0
- package/dist/backend/muse.js.map +1 -1
- package/dist/channel/adapters/discord.js +12 -1
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.js +6 -0
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/mcp-tools.js +3 -3
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +3 -0
- package/dist/cross-instance-envelope.js +2 -0
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +6 -0
- package/dist/daemon.js +124 -54
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +27 -0
- package/dist/delivery-outbox.js +175 -6
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/fleet-manager.d.ts +28 -1
- package/dist/fleet-manager.js +203 -40
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +14 -1
- package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
- package/dist/human-delivery-receipts.d.ts +83 -0
- package/dist/human-delivery-receipts.js +194 -0
- package/dist/human-delivery-receipts.js.map +1 -0
- package/dist/instructions.d.ts +1 -1
- package/dist/instructions.js +1 -1
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +8 -0
- package/dist/locale.js.map +1 -1
- package/dist/outbound-handlers.d.ts +3 -0
- package/dist/outbound-handlers.js +20 -4
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +15 -13
- package/dist/outbound-schemas.js +5 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-link-progress.d.ts +1 -1
- package/dist/public-link-progress.js +8 -2
- package/dist/public-link-progress.js.map +1 -1
- package/dist/public-preview.d.ts +2 -1
- package/dist/public-preview.js +8 -5
- package/dist/public-preview.js.map +1 -1
- package/dist/public-web-link.d.ts +4 -1
- package/dist/public-web-link.js +27 -10
- package/dist/public-web-link.js.map +1 -1
- package/dist/reply-buttons-controller.d.ts +3 -0
- package/dist/reply-buttons-controller.js +3 -2
- package/dist/reply-buttons-controller.js.map +1 -1
- package/dist/topic-commands.d.ts +3 -2
- package/dist/topic-commands.js +14 -4
- package/dist/topic-commands.js.map +1 -1
- package/dist/tunnel/cloudflared.d.ts +7 -15
- package/dist/tunnel/cloudflared.js +36 -1
- package/dist/tunnel/cloudflared.js.map +1 -1
- package/dist/tunnel/types.d.ts +5 -1
- package/dist/tunnel/types.js.map +1 -1
- package/dist/ui/chat-strings.js +8 -2
- package/dist/ui/chat-thread.js +24 -4
- package/dist/ui/chat-time.js +80 -0
- package/dist/ui/preview-panel.js +7 -1
- package/dist/ui/preview.js +5 -1
- package/dist/ui/shared/app.css +4 -1
- package/dist/web-api.d.ts +2 -0
- package/dist/web-api.js +8 -1
- package/dist/web-api.js.map +1 -1
- package/dist/web-commands.d.ts +2 -2
- package/dist/web-commands.js +3 -2
- package/dist/web-commands.js.map +1 -1
- package/dist/web-reply-files.d.ts +45 -0
- package/dist/web-reply-files.js +223 -0
- package/dist/web-reply-files.js.map +1 -0
- package/dist/web-upload.d.ts +17 -4
- package/dist/web-upload.js +64 -13
- package/dist/web-upload.js.map +1 -1
- package/package.json +1 -1
package/dist/daemon.js
CHANGED
|
@@ -33,6 +33,7 @@ import { HangDetector } from "./hang-detector.js";
|
|
|
33
33
|
import { writeSecretFile } from "./secret-file.js";
|
|
34
34
|
import { PaneWriteLock } from "./pane-write-lock.js";
|
|
35
35
|
import { buildFleetInstructions } from "./instructions.js";
|
|
36
|
+
import { formatHumanInboundMessage, prepareDeliveryWire, steerInboundEnvelope, BTW_INBOUND_PREFIX, humanBodyHash, humanOwnerObjectId } from "./human-delivery-receipts.js";
|
|
36
37
|
import { formatCrossInstanceInboundMessage, renderCrossInstanceHandoffMetadata, } from "./cross-instance-envelope.js";
|
|
37
38
|
import { agendMessageInInput, bottomRowIsReady, inputAreaText, inputShowsPastedText, pastedTextSignature, pasteLeftInInput, strandedAgendMessageInInput } from "./pane-input-residue.js";
|
|
38
39
|
import { TurnReplyGuard } from "./turn-reply-guard.js";
|
|
@@ -2003,6 +2004,7 @@ export class Daemon extends EventEmitter {
|
|
|
2003
2004
|
}
|
|
2004
2005
|
}
|
|
2005
2006
|
deferDurableDelivery(meta, reason) {
|
|
2007
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
2006
2008
|
const delivery = this.durableDeliveryAttempt(meta);
|
|
2007
2009
|
if (delivery === null || delivery === false)
|
|
2008
2010
|
return;
|
|
@@ -6322,11 +6324,7 @@ export class Daemon extends EventEmitter {
|
|
|
6322
6324
|
* is a durable delivery) must LEAD the persisted user message — restart reconciliation recognises a delivery only by a
|
|
6323
6325
|
* marker at the start of the body (#1197) — so the banner goes right after it, not in front of it.
|
|
6324
6326
|
*/
|
|
6325
|
-
static steerEnvelope(inbound) {
|
|
6326
|
-
const banner = "[STEERING — mid-task course correction. Fold this into the CURRENT work if one is active.]\n";
|
|
6327
|
-
const marker = inbound.match(/^\[agend-delivery-id:[^\]\r\n]+\]\r?\n/);
|
|
6328
|
-
return marker ? marker[0] + banner + inbound.slice(marker[0].length) : banner + inbound;
|
|
6329
|
-
}
|
|
6327
|
+
static steerEnvelope(inbound) { return steerInboundEnvelope(inbound); }
|
|
6330
6328
|
/**
|
|
6331
6329
|
* The one place an inbound message grows its metadata wrapper ([user:]/
|
|
6332
6330
|
* [from:] prefix, pending reactions, handoff metadata, reply instructions).
|
|
@@ -6337,35 +6335,65 @@ export class Daemon extends EventEmitter {
|
|
|
6337
6335
|
/** #1650: how this instance talks to the fleet — MCP tools, or agend-agent commands (agent_mode: cli). */
|
|
6338
6336
|
agentMode() { return this.config.agent_mode === "cli" ? "cli" : "mcp"; }
|
|
6339
6337
|
formatInboundMessage(content, meta) {
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
let formatted;
|
|
6343
|
-
if (
|
|
6344
|
-
formatted
|
|
6338
|
+
if (!meta.from_instance)
|
|
6339
|
+
return formatHumanInboundMessage(content, meta, this.agentMode());
|
|
6340
|
+
let formatted = formatCrossInstanceInboundMessage(content, meta, this.agentMode());
|
|
6341
|
+
if (meta.reply_to_text)
|
|
6342
|
+
formatted += '\n(reply_to: "' + meta.reply_to_text + '")';
|
|
6343
|
+
if (meta.delivery_id)
|
|
6344
|
+
formatted = "[agend-delivery-id:" + meta.delivery_id + "]\n" + formatted;
|
|
6345
|
+
return formatted;
|
|
6346
|
+
}
|
|
6347
|
+
/** Pure cached owner: no pane/process query on ingress. */
|
|
6348
|
+
captureHumanReceiptOwner() {
|
|
6349
|
+
try {
|
|
6350
|
+
if (this.deliveryWritesStopping || this.spawning || !this.tmux)
|
|
6351
|
+
return null;
|
|
6352
|
+
const o = this.interactionOwner();
|
|
6353
|
+
const window = this.tmux.getWindowId();
|
|
6354
|
+
if (!window || !o.bootId || ![o.spawnGeneration, o.launchAttempt, o.launchFenceEpoch, this.deliveryEpoch]
|
|
6355
|
+
.every(n => Number.isSafeInteger(n) && n >= 0))
|
|
6356
|
+
return null;
|
|
6357
|
+
return JSON.stringify([o.bootId, o.spawnGeneration, o.launchAttempt, o.launchFenceEpoch,
|
|
6358
|
+
this.deliveryEpoch, humanOwnerObjectId(this.tmux), window]);
|
|
6345
6359
|
}
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
}
|
|
6365
|
-
|
|
6366
|
-
|
|
6360
|
+
catch {
|
|
6361
|
+
return null;
|
|
6362
|
+
}
|
|
6363
|
+
}
|
|
6364
|
+
/** The same formatter/sanitizer used by the actual write; snapshot only. */
|
|
6365
|
+
humanReceiptWire(content, meta, kind) {
|
|
6366
|
+
let wire = kind === "raw_paste" ? content
|
|
6367
|
+
: !meta.from_instance && content.startsWith("/raw ") && kind === "fleet_inbound"
|
|
6368
|
+
? content.slice(5) : this.formatInboundMessage(content, meta);
|
|
6369
|
+
if (kind === "steer")
|
|
6370
|
+
wire = Daemon.steerEnvelope(wire);
|
|
6371
|
+
else if (kind === "btw")
|
|
6372
|
+
wire = BTW_INBOUND_PREFIX + wire;
|
|
6373
|
+
return prepareDeliveryWire(wire, kind === "raw_paste");
|
|
6374
|
+
}
|
|
6375
|
+
humanReceiptAttempt(meta, wire) {
|
|
6376
|
+
const id = meta.human_receipt_id;
|
|
6377
|
+
const daemonOwner = meta.human_daemon_owner;
|
|
6378
|
+
if (!id || !/^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/.test(id) || meta.verification_id !== "hmsg-" + id
|
|
6379
|
+
|| !daemonOwner || !meta.human_owner_key || !/^[a-f0-9]{64}$/.test(meta.human_wire_sha256 ?? "")
|
|
6380
|
+
|| humanBodyHash(prepareDeliveryWire(wire)) !== meta.human_wire_sha256)
|
|
6381
|
+
return undefined;
|
|
6382
|
+
return { receiptId: id, verificationId: meta.verification_id, expectedWireHash: meta.human_wire_sha256,
|
|
6383
|
+
ownerKey: meta.human_owner_key, current: () => this.captureHumanReceiptOwner() === daemonOwner };
|
|
6384
|
+
}
|
|
6385
|
+
finishHumanReceipt(meta, verdict) {
|
|
6386
|
+
if (!meta.human_receipt_id || !meta.human_owner_key)
|
|
6387
|
+
return;
|
|
6388
|
+
const attempt = verdict.humanReceipt;
|
|
6389
|
+
const proof = attempt?.current() && !["unverified", "output-edge", "unverifiable"].includes(verdict.proof ?? "")
|
|
6390
|
+
? attempt.proof ?? null : null;
|
|
6391
|
+
try {
|
|
6392
|
+
this.emit("human_receipt_result", { receipt_id: meta.human_receipt_id, owner_key: meta.human_owner_key, proof });
|
|
6393
|
+
}
|
|
6394
|
+
catch (err) {
|
|
6395
|
+
this.logger.error({ err }, "Could not publish human delivery evidence");
|
|
6367
6396
|
}
|
|
6368
|
-
return formatted;
|
|
6369
6397
|
}
|
|
6370
6398
|
/**
|
|
6371
6399
|
* Whether ONE delivery reached a verdict, carried by that delivery.
|
|
@@ -6458,12 +6486,13 @@ export class Daemon extends EventEmitter {
|
|
|
6458
6486
|
this.deferDurableDelivery(meta, "delivery epoch changed while waking for steer");
|
|
6459
6487
|
return;
|
|
6460
6488
|
}
|
|
6461
|
-
const verdict = { reached: false };
|
|
6489
|
+
const verdict = { reached: false, humanReceipt: this.humanReceiptAttempt(meta, formatted) };
|
|
6462
6490
|
try {
|
|
6463
6491
|
if (await this.deliverMessage(formatted, status, {
|
|
6464
6492
|
steer: true,
|
|
6465
6493
|
deliveryEpoch,
|
|
6466
6494
|
submissionId: meta.message_id,
|
|
6495
|
+
...(meta.verification_id ? { humanVerificationId: meta.verification_id } : {}),
|
|
6467
6496
|
verdict,
|
|
6468
6497
|
durableAttempt: durableAttempt || undefined,
|
|
6469
6498
|
})) {
|
|
@@ -6494,6 +6523,9 @@ export class Daemon extends EventEmitter {
|
|
|
6494
6523
|
this.retryDurableDeliveryBeforeBegin(durableAttempt, err.message);
|
|
6495
6524
|
throw err;
|
|
6496
6525
|
}
|
|
6526
|
+
finally {
|
|
6527
|
+
this.finishHumanReceipt(meta, verdict);
|
|
6528
|
+
}
|
|
6497
6529
|
}).catch(err => {
|
|
6498
6530
|
this.deferDurableDelivery(meta, `target wake/steer failed before pane submission: ${err.message}`);
|
|
6499
6531
|
this.logger.warn({ err: err.message }, "steer delivery error");
|
|
@@ -6509,24 +6541,37 @@ export class Daemon extends EventEmitter {
|
|
|
6509
6541
|
* so channel delivery intentionally uses a normal wrapped inbound instead.
|
|
6510
6542
|
*/
|
|
6511
6543
|
btwMessage(content, meta, deliveryEpoch = this.deliveryEpoch) {
|
|
6512
|
-
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6544
|
+
if (!this.isDeliveryEpochCurrent(deliveryEpoch)) {
|
|
6545
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6513
6546
|
return;
|
|
6547
|
+
}
|
|
6514
6548
|
this.updateLastChat(meta.chat_id, meta.thread_id, meta.adapter_id);
|
|
6515
6549
|
this.pendingWork.recordInbound();
|
|
6516
6550
|
this.recordRecentUserMessage(content, meta);
|
|
6517
|
-
const formatted =
|
|
6518
|
-
+ this.formatInboundMessage(content, meta);
|
|
6551
|
+
const formatted = BTW_INBOUND_PREFIX + this.formatInboundMessage(content, meta);
|
|
6519
6552
|
const status = channelStatus(meta);
|
|
6520
6553
|
this.steerLock = this.steerLock.then(async () => {
|
|
6521
|
-
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6554
|
+
if (!this.isDeliveryEpochCurrent(deliveryEpoch)) {
|
|
6555
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6522
6556
|
return;
|
|
6557
|
+
}
|
|
6523
6558
|
await this.wake();
|
|
6524
|
-
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6559
|
+
if (!this.isDeliveryEpochCurrent(deliveryEpoch)) {
|
|
6560
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6525
6561
|
return;
|
|
6526
|
-
|
|
6527
|
-
|
|
6562
|
+
}
|
|
6563
|
+
const verdict = { reached: false, humanReceipt: this.humanReceiptAttempt(meta, formatted) };
|
|
6564
|
+
try {
|
|
6565
|
+
if (await this.deliverMessage(formatted, status, { steer: true, deliveryEpoch, submissionId: meta.message_id,
|
|
6566
|
+
...(meta.verification_id ? { humanVerificationId: meta.verification_id } : {}), verdict })) {
|
|
6567
|
+
this.markTurnStarted(meta, formatted, deliveryEpoch);
|
|
6568
|
+
}
|
|
6569
|
+
}
|
|
6570
|
+
finally {
|
|
6571
|
+
this.finishHumanReceipt(meta, verdict);
|
|
6528
6572
|
}
|
|
6529
6573
|
}).catch(err => {
|
|
6574
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6530
6575
|
this.logger.warn({ err: err.message }, "btw delivery error");
|
|
6531
6576
|
});
|
|
6532
6577
|
}
|
|
@@ -6640,7 +6685,7 @@ export class Daemon extends EventEmitter {
|
|
|
6640
6685
|
// A fresh delivery begins a fresh turn — its bubble must not inherit
|
|
6641
6686
|
// the previous turn's tool list.
|
|
6642
6687
|
this.resetToolProgress();
|
|
6643
|
-
verdict = { reached: false };
|
|
6688
|
+
verdict = { reached: false, humanReceipt: this.humanReceiptAttempt(meta, formatted) };
|
|
6644
6689
|
const durableAttempt = this.durableDeliveryAttempt(meta, content);
|
|
6645
6690
|
if (durableAttempt === false) {
|
|
6646
6691
|
this.deferDurableDelivery(meta, "invalid durable delivery attempt metadata");
|
|
@@ -6650,6 +6695,7 @@ export class Daemon extends EventEmitter {
|
|
|
6650
6695
|
if (await this.deliverMessage(formatted, status, {
|
|
6651
6696
|
deliveryEpoch,
|
|
6652
6697
|
submissionId: meta.message_id,
|
|
6698
|
+
...(meta.verification_id ? { humanVerificationId: meta.verification_id } : {}),
|
|
6653
6699
|
verdict,
|
|
6654
6700
|
durableAttempt: durableAttempt || undefined,
|
|
6655
6701
|
})) {
|
|
@@ -6697,6 +6743,7 @@ export class Daemon extends EventEmitter {
|
|
|
6697
6743
|
this.logger.warn({ err: err.message }, "Durable pane delivery deferred after exception");
|
|
6698
6744
|
}
|
|
6699
6745
|
finally {
|
|
6746
|
+
this.finishHumanReceipt(meta, verdict);
|
|
6700
6747
|
this.pasteQueueDepth--;
|
|
6701
6748
|
}
|
|
6702
6749
|
}).catch(err => {
|
|
@@ -6733,12 +6780,7 @@ export class Daemon extends EventEmitter {
|
|
|
6733
6780
|
&& !this.isDeliveryEpochCurrent(opts.deliveryEpoch);
|
|
6734
6781
|
if (cancelled() || this.stormWindow?.isStopped())
|
|
6735
6782
|
return false;
|
|
6736
|
-
|
|
6737
|
-
const fenceCount = (formatted.match(/```/g) || []).length;
|
|
6738
|
-
if (!opts?.preserveExactBytes && fenceCount % 2 !== 0) {
|
|
6739
|
-
// Odd number of fences = unclosed. Remove all code fences from the message.
|
|
6740
|
-
formatted = formatted.replace(/```/g, "");
|
|
6741
|
-
}
|
|
6783
|
+
formatted = prepareDeliveryWire(formatted, opts?.preserveExactBytes);
|
|
6742
6784
|
// Direct paths such as /steer, /btw, /raw and schedules do not pass the
|
|
6743
6785
|
// FleetManager idle gate. Hold them at the final common pane-write path so
|
|
6744
6786
|
// a tmux storm cannot force any text into a half-booted CLI.
|
|
@@ -6911,7 +6953,7 @@ export class Daemon extends EventEmitter {
|
|
|
6911
6953
|
return stale();
|
|
6912
6954
|
if (residue !== "clear")
|
|
6913
6955
|
return this.failDelivery(verdict, status, "input-residue", residue);
|
|
6914
|
-
const written = await this.writeMessageToPane(formatted, windowId, handingOffToNativeQueue, status, opts?.submissionId, verdict, opts?.durableAttempt, opts?.steer === true, writeCurrent);
|
|
6956
|
+
const written = await this.writeMessageToPane(formatted, windowId, handingOffToNativeQueue, status, opts?.submissionId, verdict, opts?.durableAttempt, opts?.steer === true, writeCurrent, opts?.humanVerificationId);
|
|
6915
6957
|
// #1690: still under the lock — whatever waits on it next must already see what this write started.
|
|
6916
6958
|
if (verdict.paneWriteStarted)
|
|
6917
6959
|
opts?.onPaneWritten?.();
|
|
@@ -7706,7 +7748,7 @@ export class Daemon extends EventEmitter {
|
|
|
7706
7748
|
return false;
|
|
7707
7749
|
const row = norm(shown);
|
|
7708
7750
|
// The marker line holds nothing but the marker: the row is exactly it.
|
|
7709
|
-
if (/^\[agend-delivery-id:[^\]\s]+\]$/.test(first))
|
|
7751
|
+
if (/^\[agend-(?:delivery|human)-id:[^\]\s]+\]$/.test(first))
|
|
7710
7752
|
return row === first;
|
|
7711
7753
|
return row === first || row.startsWith(`${first} `);
|
|
7712
7754
|
}
|
|
@@ -8248,6 +8290,8 @@ export class Daemon extends EventEmitter {
|
|
|
8248
8290
|
if (!current())
|
|
8249
8291
|
return false;
|
|
8250
8292
|
if (evidence === "submitted") {
|
|
8293
|
+
if (pending.signature.unique)
|
|
8294
|
+
pending.signature.humanPositive?.("unique-id-pane");
|
|
8251
8295
|
if (status)
|
|
8252
8296
|
this.emit("message_confirmed", status);
|
|
8253
8297
|
return true;
|
|
@@ -8283,6 +8327,8 @@ export class Daemon extends EventEmitter {
|
|
|
8283
8327
|
if (retry === "cancelled")
|
|
8284
8328
|
return false;
|
|
8285
8329
|
if (retry === "submitted") {
|
|
8330
|
+
if (pending.signature.unique)
|
|
8331
|
+
pending.signature.humanPositive?.("unique-id-pane");
|
|
8286
8332
|
if (status)
|
|
8287
8333
|
this.emit("message_confirmed", status);
|
|
8288
8334
|
return true;
|
|
@@ -8423,9 +8469,24 @@ export class Daemon extends EventEmitter {
|
|
|
8423
8469
|
verdict = { reached: false }, durableAttempt, steer = false,
|
|
8424
8470
|
// #829: the caller's fence, asked once more after the last await before the
|
|
8425
8471
|
// first side effect. Stale means unwritten: false, with no failure verdict.
|
|
8426
|
-
stillCurrent) {
|
|
8427
|
-
const signature = this.submissionSignature(formatted, submissionId);
|
|
8472
|
+
stillCurrent, humanVerificationId) {
|
|
8473
|
+
const signature = this.submissionSignature(formatted, humanVerificationId ?? submissionId, humanVerificationId !== undefined);
|
|
8428
8474
|
const rawPaste = durableAttempt?.submissionMode === "raw_paste";
|
|
8475
|
+
const humanAttempt = verdict.humanReceipt;
|
|
8476
|
+
const humanMode = rawPaste ? "raw_paste" : steer ? "steer"
|
|
8477
|
+
: handingOffToNativeQueue ? "native_queue_handoff" : "idle_submit";
|
|
8478
|
+
if (humanAttempt && signature.unique && signature.value === "agend-human-id:" + humanAttempt.verificationId
|
|
8479
|
+
&& humanBodyHash(formatted) === humanAttempt.expectedWireHash) {
|
|
8480
|
+
signature.humanPositive = tag => {
|
|
8481
|
+
if (!humanAttempt.current() || (stillCurrent && !stillCurrent()))
|
|
8482
|
+
return;
|
|
8483
|
+
humanAttempt.proof = {
|
|
8484
|
+
receipt_id: humanAttempt.receiptId, verification_id: humanAttempt.verificationId,
|
|
8485
|
+
wire_sha256: humanBodyHash(formatted), owner_key: humanAttempt.ownerKey,
|
|
8486
|
+
mode: humanMode, tag,
|
|
8487
|
+
};
|
|
8488
|
+
};
|
|
8489
|
+
}
|
|
8429
8490
|
let windowId = initialWindowId;
|
|
8430
8491
|
// The spawn this write belongs to: acceptance evidence is only read while it is still the current one (#1197).
|
|
8431
8492
|
const spawnAtWrite = this.spawnGeneration;
|
|
@@ -8571,6 +8632,7 @@ export class Daemon extends EventEmitter {
|
|
|
8571
8632
|
// positively hold typed text, and after it be the empty placeholder again. The empty placeholder here means the
|
|
8572
8633
|
// paste never reached the box; an unreadable box (a failed or late capture, a dialog) proves nothing: no Enter.
|
|
8573
8634
|
const steerComposerProof = handingOffToNativeQueue && steer && !!this.backend?.readSteerComposer && tmuxAtWrite !== null;
|
|
8635
|
+
let humanComposerOwned = false;
|
|
8574
8636
|
let steerEnterDeadline = Infinity;
|
|
8575
8637
|
if (steerComposerProof) {
|
|
8576
8638
|
const deadline = performance.now() + STEER_PASTE_VISIBLE_MS;
|
|
@@ -8594,6 +8656,8 @@ export class Daemon extends EventEmitter {
|
|
|
8594
8656
|
return this.failDelivery(verdict, status, "steer-paste", before === null ? "box-unread" : `box-still-${before.mode}`);
|
|
8595
8657
|
if (!this.steerPasteIsOurs(before.pane, formatted))
|
|
8596
8658
|
return this.failDelivery(verdict, status, "steer-paste", "box-not-this-paste");
|
|
8659
|
+
const shown = this.backend?.readSteerComposerText?.(before.pane)?.replace(/\s+/g, " ").trim();
|
|
8660
|
+
humanComposerOwned = !!humanAttempt && shown === "[agend-human-id:" + humanAttempt.verificationId + "]";
|
|
8597
8661
|
// …and so is the Enter: the key is sent only while the reading is still within it.
|
|
8598
8662
|
steerEnterDeadline = deadline;
|
|
8599
8663
|
}
|
|
@@ -8665,6 +8729,9 @@ export class Daemon extends EventEmitter {
|
|
|
8665
8729
|
if (after === "steer" || after === "queue" || after === "idle") {
|
|
8666
8730
|
const probe = await withinMs(this.probeBlockingDialog(), deadline - performance.now());
|
|
8667
8731
|
if (probe?.state === "clear" && performance.now() <= deadline && steerCurrent() && this.getWindowId() === windowId) {
|
|
8732
|
+
if (humanComposerOwned)
|
|
8733
|
+
signature.humanPositive?.(after === "steer" ? "composer-empty-steer"
|
|
8734
|
+
: after === "queue" ? "composer-empty-queue" : "composer-empty-idle");
|
|
8668
8735
|
verdict.phase = "steer-accepted";
|
|
8669
8736
|
verdict.proof = `steer-composer-${after}`;
|
|
8670
8737
|
if (status)
|
|
@@ -9251,8 +9318,11 @@ export class Daemon extends EventEmitter {
|
|
|
9251
9318
|
// the input row is proof in itself — which also means a momentary
|
|
9252
9319
|
// failure to read the pane BEFORE pasting cannot turn a delivered
|
|
9253
9320
|
// message into a re-paste.
|
|
9254
|
-
if (signature.unique && after.payload > 0)
|
|
9321
|
+
if (signature.unique && after.payload > 0) {
|
|
9322
|
+
if (after.inputReadable)
|
|
9323
|
+
signature.humanPositive?.("unique-id-pane");
|
|
9255
9324
|
return this.submittedProof();
|
|
9325
|
+
}
|
|
9256
9326
|
if (unattributedResidue)
|
|
9257
9327
|
return "unverifiable";
|
|
9258
9328
|
// 3. Otherwise the evidence must be NEW relative to the pane as it was
|
|
@@ -9389,7 +9459,7 @@ export class Daemon extends EventEmitter {
|
|
|
9389
9459
|
* paste that never landed. System pastes carry no envelope and fall back to
|
|
9390
9460
|
* the body signature, which the baseline comparison still ties to this paste.
|
|
9391
9461
|
*/
|
|
9392
|
-
submissionSignature(formatted, trustedId) {
|
|
9462
|
+
submissionSignature(formatted, trustedId, verification = false) {
|
|
9393
9463
|
// The id must come from the message's own metadata, never from scanning the
|
|
9394
9464
|
// rendered text: agents and users discuss message ids in the body all the
|
|
9395
9465
|
// time ("check message_id: abc"), and a body-scanned value would match an
|
|
@@ -9398,7 +9468,7 @@ export class Daemon extends EventEmitter {
|
|
|
9398
9468
|
// the handoff metadata identifies THIS delivery.
|
|
9399
9469
|
const id = trustedId?.trim();
|
|
9400
9470
|
if (id)
|
|
9401
|
-
return { value:
|
|
9471
|
+
return { value: (verification ? "agend-human-id:" : "message_id:") + id, unique: true };
|
|
9402
9472
|
return { value: pastedTextSignature(formatted), unique: false };
|
|
9403
9473
|
}
|
|
9404
9474
|
/**
|