@songsid/agend 2.2.0-beta.6 → 2.2.0-beta.8
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/agent-whats-new.js +9 -3
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/backend/kiro.d.ts +9 -0
- package/dist/backend/kiro.js +23 -0
- package/dist/backend/kiro.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/config-validator.js +3 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/cross-instance-envelope.js +2 -0
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +8 -0
- package/dist/daemon.js +126 -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 +40 -1
- package/dist/fleet-manager.js +248 -40
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -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/instance-config-impact.js +1 -0
- package/dist/instance-config-impact.js.map +1 -1
- 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/log-service.d.ts +99 -0
- package/dist/log-service.js +235 -0
- package/dist/log-service.js.map +1 -0
- package/dist/log-steps.d.ts +143 -0
- package/dist/log-steps.js +646 -0
- package/dist/log-steps.js.map +1 -0
- 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-gateway.js +1 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/public-web-link.d.ts +4 -1
- package/dist/public-web-link.js +42 -13
- 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/settings-api.js +4 -0
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +2 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +2 -0
- package/dist/settings-effect.js.map +1 -1
- package/dist/tool-progress.js +3 -2
- package/dist/tool-progress.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/transcript-jsonl.d.ts +80 -3
- package/dist/transcript-jsonl.js +147 -10
- package/dist/transcript-jsonl.js.map +1 -1
- package/dist/transcript-monitor.d.ts +11 -0
- package/dist/transcript-monitor.js +61 -13
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-sources.d.ts +24 -0
- package/dist/transcript-sources.js +205 -55
- package/dist/transcript-sources.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/types.d.ts +6 -0
- 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/panel-log.js +388 -0
- package/dist/ui/panel-settings.js +6 -1
- package/dist/ui/preview-panel.js +7 -1
- package/dist/ui/preview.js +5 -1
- package/dist/ui/settings-strings.js +2 -0
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-route.js +6 -1
- package/dist/ui/shared/app-shell.js +3 -3
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +44 -6
- package/dist/ui/shared/app.js +5 -0
- package/dist/ui/shared/fonts/README.md +1 -1
- package/dist/ui/shared/instance-nav.js +9 -6
- package/dist/ui/shared/instance-switch.js +4 -4
- package/dist/ui/shared/tokens.css +1 -1
- package/dist/web-api.d.ts +5 -0
- package/dist/web-api.js +49 -1
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +2 -1
- package/dist/web-auth.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-settings-patch.d.ts +3 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -0
- package/dist/web-shell-routes.js +9 -0
- package/dist/web-shell-routes.js.map +1 -1
- 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";
|
|
@@ -1088,6 +1089,8 @@ export class Daemon extends EventEmitter {
|
|
|
1088
1089
|
ipcServer = null;
|
|
1089
1090
|
messageBus;
|
|
1090
1091
|
transcriptMonitor = null;
|
|
1092
|
+
/** #1218: the transcript this instance's monitor follows now (the Log tab reads it on demand; nothing runs for it here). */
|
|
1093
|
+
logTranscriptPath() { return this.transcriptMonitor?.currentPath() ?? null; }
|
|
1091
1094
|
guardian = null;
|
|
1092
1095
|
adapter = null;
|
|
1093
1096
|
pendingIpcRequests = new Map();
|
|
@@ -2003,6 +2006,7 @@ export class Daemon extends EventEmitter {
|
|
|
2003
2006
|
}
|
|
2004
2007
|
}
|
|
2005
2008
|
deferDurableDelivery(meta, reason) {
|
|
2009
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
2006
2010
|
const delivery = this.durableDeliveryAttempt(meta);
|
|
2007
2011
|
if (delivery === null || delivery === false)
|
|
2008
2012
|
return;
|
|
@@ -6322,11 +6326,7 @@ export class Daemon extends EventEmitter {
|
|
|
6322
6326
|
* is a durable delivery) must LEAD the persisted user message — restart reconciliation recognises a delivery only by a
|
|
6323
6327
|
* marker at the start of the body (#1197) — so the banner goes right after it, not in front of it.
|
|
6324
6328
|
*/
|
|
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
|
-
}
|
|
6329
|
+
static steerEnvelope(inbound) { return steerInboundEnvelope(inbound); }
|
|
6330
6330
|
/**
|
|
6331
6331
|
* The one place an inbound message grows its metadata wrapper ([user:]/
|
|
6332
6332
|
* [from:] prefix, pending reactions, handoff metadata, reply instructions).
|
|
@@ -6337,35 +6337,65 @@ export class Daemon extends EventEmitter {
|
|
|
6337
6337
|
/** #1650: how this instance talks to the fleet — MCP tools, or agend-agent commands (agent_mode: cli). */
|
|
6338
6338
|
agentMode() { return this.config.agent_mode === "cli" ? "cli" : "mcp"; }
|
|
6339
6339
|
formatInboundMessage(content, meta) {
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
let formatted;
|
|
6343
|
-
if (
|
|
6344
|
-
formatted
|
|
6340
|
+
if (!meta.from_instance)
|
|
6341
|
+
return formatHumanInboundMessage(content, meta, this.agentMode());
|
|
6342
|
+
let formatted = formatCrossInstanceInboundMessage(content, meta, this.agentMode());
|
|
6343
|
+
if (meta.reply_to_text)
|
|
6344
|
+
formatted += '\n(reply_to: "' + meta.reply_to_text + '")';
|
|
6345
|
+
if (meta.delivery_id)
|
|
6346
|
+
formatted = "[agend-delivery-id:" + meta.delivery_id + "]\n" + formatted;
|
|
6347
|
+
return formatted;
|
|
6348
|
+
}
|
|
6349
|
+
/** Pure cached owner: no pane/process query on ingress. */
|
|
6350
|
+
captureHumanReceiptOwner() {
|
|
6351
|
+
try {
|
|
6352
|
+
if (this.deliveryWritesStopping || this.spawning || !this.tmux)
|
|
6353
|
+
return null;
|
|
6354
|
+
const o = this.interactionOwner();
|
|
6355
|
+
const window = this.tmux.getWindowId();
|
|
6356
|
+
if (!window || !o.bootId || ![o.spawnGeneration, o.launchAttempt, o.launchFenceEpoch, this.deliveryEpoch]
|
|
6357
|
+
.every(n => Number.isSafeInteger(n) && n >= 0))
|
|
6358
|
+
return null;
|
|
6359
|
+
return JSON.stringify([o.bootId, o.spawnGeneration, o.launchAttempt, o.launchFenceEpoch,
|
|
6360
|
+
this.deliveryEpoch, humanOwnerObjectId(this.tmux), window]);
|
|
6345
6361
|
}
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
}
|
|
6365
|
-
|
|
6366
|
-
|
|
6362
|
+
catch {
|
|
6363
|
+
return null;
|
|
6364
|
+
}
|
|
6365
|
+
}
|
|
6366
|
+
/** The same formatter/sanitizer used by the actual write; snapshot only. */
|
|
6367
|
+
humanReceiptWire(content, meta, kind) {
|
|
6368
|
+
let wire = kind === "raw_paste" ? content
|
|
6369
|
+
: !meta.from_instance && content.startsWith("/raw ") && kind === "fleet_inbound"
|
|
6370
|
+
? content.slice(5) : this.formatInboundMessage(content, meta);
|
|
6371
|
+
if (kind === "steer")
|
|
6372
|
+
wire = Daemon.steerEnvelope(wire);
|
|
6373
|
+
else if (kind === "btw")
|
|
6374
|
+
wire = BTW_INBOUND_PREFIX + wire;
|
|
6375
|
+
return prepareDeliveryWire(wire, kind === "raw_paste");
|
|
6376
|
+
}
|
|
6377
|
+
humanReceiptAttempt(meta, wire) {
|
|
6378
|
+
const id = meta.human_receipt_id;
|
|
6379
|
+
const daemonOwner = meta.human_daemon_owner;
|
|
6380
|
+
if (!id || !/^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/.test(id) || meta.verification_id !== "hmsg-" + id
|
|
6381
|
+
|| !daemonOwner || !meta.human_owner_key || !/^[a-f0-9]{64}$/.test(meta.human_wire_sha256 ?? "")
|
|
6382
|
+
|| humanBodyHash(prepareDeliveryWire(wire)) !== meta.human_wire_sha256)
|
|
6383
|
+
return undefined;
|
|
6384
|
+
return { receiptId: id, verificationId: meta.verification_id, expectedWireHash: meta.human_wire_sha256,
|
|
6385
|
+
ownerKey: meta.human_owner_key, current: () => this.captureHumanReceiptOwner() === daemonOwner };
|
|
6386
|
+
}
|
|
6387
|
+
finishHumanReceipt(meta, verdict) {
|
|
6388
|
+
if (!meta.human_receipt_id || !meta.human_owner_key)
|
|
6389
|
+
return;
|
|
6390
|
+
const attempt = verdict.humanReceipt;
|
|
6391
|
+
const proof = attempt?.current() && !["unverified", "output-edge", "unverifiable"].includes(verdict.proof ?? "")
|
|
6392
|
+
? attempt.proof ?? null : null;
|
|
6393
|
+
try {
|
|
6394
|
+
this.emit("human_receipt_result", { receipt_id: meta.human_receipt_id, owner_key: meta.human_owner_key, proof });
|
|
6395
|
+
}
|
|
6396
|
+
catch (err) {
|
|
6397
|
+
this.logger.error({ err }, "Could not publish human delivery evidence");
|
|
6367
6398
|
}
|
|
6368
|
-
return formatted;
|
|
6369
6399
|
}
|
|
6370
6400
|
/**
|
|
6371
6401
|
* Whether ONE delivery reached a verdict, carried by that delivery.
|
|
@@ -6458,12 +6488,13 @@ export class Daemon extends EventEmitter {
|
|
|
6458
6488
|
this.deferDurableDelivery(meta, "delivery epoch changed while waking for steer");
|
|
6459
6489
|
return;
|
|
6460
6490
|
}
|
|
6461
|
-
const verdict = { reached: false };
|
|
6491
|
+
const verdict = { reached: false, humanReceipt: this.humanReceiptAttempt(meta, formatted) };
|
|
6462
6492
|
try {
|
|
6463
6493
|
if (await this.deliverMessage(formatted, status, {
|
|
6464
6494
|
steer: true,
|
|
6465
6495
|
deliveryEpoch,
|
|
6466
6496
|
submissionId: meta.message_id,
|
|
6497
|
+
...(meta.verification_id ? { humanVerificationId: meta.verification_id } : {}),
|
|
6467
6498
|
verdict,
|
|
6468
6499
|
durableAttempt: durableAttempt || undefined,
|
|
6469
6500
|
})) {
|
|
@@ -6494,6 +6525,9 @@ export class Daemon extends EventEmitter {
|
|
|
6494
6525
|
this.retryDurableDeliveryBeforeBegin(durableAttempt, err.message);
|
|
6495
6526
|
throw err;
|
|
6496
6527
|
}
|
|
6528
|
+
finally {
|
|
6529
|
+
this.finishHumanReceipt(meta, verdict);
|
|
6530
|
+
}
|
|
6497
6531
|
}).catch(err => {
|
|
6498
6532
|
this.deferDurableDelivery(meta, `target wake/steer failed before pane submission: ${err.message}`);
|
|
6499
6533
|
this.logger.warn({ err: err.message }, "steer delivery error");
|
|
@@ -6509,24 +6543,37 @@ export class Daemon extends EventEmitter {
|
|
|
6509
6543
|
* so channel delivery intentionally uses a normal wrapped inbound instead.
|
|
6510
6544
|
*/
|
|
6511
6545
|
btwMessage(content, meta, deliveryEpoch = this.deliveryEpoch) {
|
|
6512
|
-
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6546
|
+
if (!this.isDeliveryEpochCurrent(deliveryEpoch)) {
|
|
6547
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6513
6548
|
return;
|
|
6549
|
+
}
|
|
6514
6550
|
this.updateLastChat(meta.chat_id, meta.thread_id, meta.adapter_id);
|
|
6515
6551
|
this.pendingWork.recordInbound();
|
|
6516
6552
|
this.recordRecentUserMessage(content, meta);
|
|
6517
|
-
const formatted =
|
|
6518
|
-
+ this.formatInboundMessage(content, meta);
|
|
6553
|
+
const formatted = BTW_INBOUND_PREFIX + this.formatInboundMessage(content, meta);
|
|
6519
6554
|
const status = channelStatus(meta);
|
|
6520
6555
|
this.steerLock = this.steerLock.then(async () => {
|
|
6521
|
-
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6556
|
+
if (!this.isDeliveryEpochCurrent(deliveryEpoch)) {
|
|
6557
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6522
6558
|
return;
|
|
6559
|
+
}
|
|
6523
6560
|
await this.wake();
|
|
6524
|
-
if (!this.isDeliveryEpochCurrent(deliveryEpoch))
|
|
6561
|
+
if (!this.isDeliveryEpochCurrent(deliveryEpoch)) {
|
|
6562
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6525
6563
|
return;
|
|
6526
|
-
|
|
6527
|
-
|
|
6564
|
+
}
|
|
6565
|
+
const verdict = { reached: false, humanReceipt: this.humanReceiptAttempt(meta, formatted) };
|
|
6566
|
+
try {
|
|
6567
|
+
if (await this.deliverMessage(formatted, status, { steer: true, deliveryEpoch, submissionId: meta.message_id,
|
|
6568
|
+
...(meta.verification_id ? { humanVerificationId: meta.verification_id } : {}), verdict })) {
|
|
6569
|
+
this.markTurnStarted(meta, formatted, deliveryEpoch);
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
finally {
|
|
6573
|
+
this.finishHumanReceipt(meta, verdict);
|
|
6528
6574
|
}
|
|
6529
6575
|
}).catch(err => {
|
|
6576
|
+
this.finishHumanReceipt(meta, { reached: false });
|
|
6530
6577
|
this.logger.warn({ err: err.message }, "btw delivery error");
|
|
6531
6578
|
});
|
|
6532
6579
|
}
|
|
@@ -6640,7 +6687,7 @@ export class Daemon extends EventEmitter {
|
|
|
6640
6687
|
// A fresh delivery begins a fresh turn — its bubble must not inherit
|
|
6641
6688
|
// the previous turn's tool list.
|
|
6642
6689
|
this.resetToolProgress();
|
|
6643
|
-
verdict = { reached: false };
|
|
6690
|
+
verdict = { reached: false, humanReceipt: this.humanReceiptAttempt(meta, formatted) };
|
|
6644
6691
|
const durableAttempt = this.durableDeliveryAttempt(meta, content);
|
|
6645
6692
|
if (durableAttempt === false) {
|
|
6646
6693
|
this.deferDurableDelivery(meta, "invalid durable delivery attempt metadata");
|
|
@@ -6650,6 +6697,7 @@ export class Daemon extends EventEmitter {
|
|
|
6650
6697
|
if (await this.deliverMessage(formatted, status, {
|
|
6651
6698
|
deliveryEpoch,
|
|
6652
6699
|
submissionId: meta.message_id,
|
|
6700
|
+
...(meta.verification_id ? { humanVerificationId: meta.verification_id } : {}),
|
|
6653
6701
|
verdict,
|
|
6654
6702
|
durableAttempt: durableAttempt || undefined,
|
|
6655
6703
|
})) {
|
|
@@ -6697,6 +6745,7 @@ export class Daemon extends EventEmitter {
|
|
|
6697
6745
|
this.logger.warn({ err: err.message }, "Durable pane delivery deferred after exception");
|
|
6698
6746
|
}
|
|
6699
6747
|
finally {
|
|
6748
|
+
this.finishHumanReceipt(meta, verdict);
|
|
6700
6749
|
this.pasteQueueDepth--;
|
|
6701
6750
|
}
|
|
6702
6751
|
}).catch(err => {
|
|
@@ -6733,12 +6782,7 @@ export class Daemon extends EventEmitter {
|
|
|
6733
6782
|
&& !this.isDeliveryEpochCurrent(opts.deliveryEpoch);
|
|
6734
6783
|
if (cancelled() || this.stormWindow?.isStopped())
|
|
6735
6784
|
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
|
-
}
|
|
6785
|
+
formatted = prepareDeliveryWire(formatted, opts?.preserveExactBytes);
|
|
6742
6786
|
// Direct paths such as /steer, /btw, /raw and schedules do not pass the
|
|
6743
6787
|
// FleetManager idle gate. Hold them at the final common pane-write path so
|
|
6744
6788
|
// a tmux storm cannot force any text into a half-booted CLI.
|
|
@@ -6911,7 +6955,7 @@ export class Daemon extends EventEmitter {
|
|
|
6911
6955
|
return stale();
|
|
6912
6956
|
if (residue !== "clear")
|
|
6913
6957
|
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);
|
|
6958
|
+
const written = await this.writeMessageToPane(formatted, windowId, handingOffToNativeQueue, status, opts?.submissionId, verdict, opts?.durableAttempt, opts?.steer === true, writeCurrent, opts?.humanVerificationId);
|
|
6915
6959
|
// #1690: still under the lock — whatever waits on it next must already see what this write started.
|
|
6916
6960
|
if (verdict.paneWriteStarted)
|
|
6917
6961
|
opts?.onPaneWritten?.();
|
|
@@ -7706,7 +7750,7 @@ export class Daemon extends EventEmitter {
|
|
|
7706
7750
|
return false;
|
|
7707
7751
|
const row = norm(shown);
|
|
7708
7752
|
// The marker line holds nothing but the marker: the row is exactly it.
|
|
7709
|
-
if (/^\[agend-delivery-id:[^\]\s]+\]$/.test(first))
|
|
7753
|
+
if (/^\[agend-(?:delivery|human)-id:[^\]\s]+\]$/.test(first))
|
|
7710
7754
|
return row === first;
|
|
7711
7755
|
return row === first || row.startsWith(`${first} `);
|
|
7712
7756
|
}
|
|
@@ -8248,6 +8292,8 @@ export class Daemon extends EventEmitter {
|
|
|
8248
8292
|
if (!current())
|
|
8249
8293
|
return false;
|
|
8250
8294
|
if (evidence === "submitted") {
|
|
8295
|
+
if (pending.signature.unique)
|
|
8296
|
+
pending.signature.humanPositive?.("unique-id-pane");
|
|
8251
8297
|
if (status)
|
|
8252
8298
|
this.emit("message_confirmed", status);
|
|
8253
8299
|
return true;
|
|
@@ -8283,6 +8329,8 @@ export class Daemon extends EventEmitter {
|
|
|
8283
8329
|
if (retry === "cancelled")
|
|
8284
8330
|
return false;
|
|
8285
8331
|
if (retry === "submitted") {
|
|
8332
|
+
if (pending.signature.unique)
|
|
8333
|
+
pending.signature.humanPositive?.("unique-id-pane");
|
|
8286
8334
|
if (status)
|
|
8287
8335
|
this.emit("message_confirmed", status);
|
|
8288
8336
|
return true;
|
|
@@ -8423,9 +8471,24 @@ export class Daemon extends EventEmitter {
|
|
|
8423
8471
|
verdict = { reached: false }, durableAttempt, steer = false,
|
|
8424
8472
|
// #829: the caller's fence, asked once more after the last await before the
|
|
8425
8473
|
// first side effect. Stale means unwritten: false, with no failure verdict.
|
|
8426
|
-
stillCurrent) {
|
|
8427
|
-
const signature = this.submissionSignature(formatted, submissionId);
|
|
8474
|
+
stillCurrent, humanVerificationId) {
|
|
8475
|
+
const signature = this.submissionSignature(formatted, humanVerificationId ?? submissionId, humanVerificationId !== undefined);
|
|
8428
8476
|
const rawPaste = durableAttempt?.submissionMode === "raw_paste";
|
|
8477
|
+
const humanAttempt = verdict.humanReceipt;
|
|
8478
|
+
const humanMode = rawPaste ? "raw_paste" : steer ? "steer"
|
|
8479
|
+
: handingOffToNativeQueue ? "native_queue_handoff" : "idle_submit";
|
|
8480
|
+
if (humanAttempt && signature.unique && signature.value === "agend-human-id:" + humanAttempt.verificationId
|
|
8481
|
+
&& humanBodyHash(formatted) === humanAttempt.expectedWireHash) {
|
|
8482
|
+
signature.humanPositive = tag => {
|
|
8483
|
+
if (!humanAttempt.current() || (stillCurrent && !stillCurrent()))
|
|
8484
|
+
return;
|
|
8485
|
+
humanAttempt.proof = {
|
|
8486
|
+
receipt_id: humanAttempt.receiptId, verification_id: humanAttempt.verificationId,
|
|
8487
|
+
wire_sha256: humanBodyHash(formatted), owner_key: humanAttempt.ownerKey,
|
|
8488
|
+
mode: humanMode, tag,
|
|
8489
|
+
};
|
|
8490
|
+
};
|
|
8491
|
+
}
|
|
8429
8492
|
let windowId = initialWindowId;
|
|
8430
8493
|
// The spawn this write belongs to: acceptance evidence is only read while it is still the current one (#1197).
|
|
8431
8494
|
const spawnAtWrite = this.spawnGeneration;
|
|
@@ -8571,6 +8634,7 @@ export class Daemon extends EventEmitter {
|
|
|
8571
8634
|
// positively hold typed text, and after it be the empty placeholder again. The empty placeholder here means the
|
|
8572
8635
|
// paste never reached the box; an unreadable box (a failed or late capture, a dialog) proves nothing: no Enter.
|
|
8573
8636
|
const steerComposerProof = handingOffToNativeQueue && steer && !!this.backend?.readSteerComposer && tmuxAtWrite !== null;
|
|
8637
|
+
let humanComposerOwned = false;
|
|
8574
8638
|
let steerEnterDeadline = Infinity;
|
|
8575
8639
|
if (steerComposerProof) {
|
|
8576
8640
|
const deadline = performance.now() + STEER_PASTE_VISIBLE_MS;
|
|
@@ -8594,6 +8658,8 @@ export class Daemon extends EventEmitter {
|
|
|
8594
8658
|
return this.failDelivery(verdict, status, "steer-paste", before === null ? "box-unread" : `box-still-${before.mode}`);
|
|
8595
8659
|
if (!this.steerPasteIsOurs(before.pane, formatted))
|
|
8596
8660
|
return this.failDelivery(verdict, status, "steer-paste", "box-not-this-paste");
|
|
8661
|
+
const shown = this.backend?.readSteerComposerText?.(before.pane)?.replace(/\s+/g, " ").trim();
|
|
8662
|
+
humanComposerOwned = !!humanAttempt && shown === "[agend-human-id:" + humanAttempt.verificationId + "]";
|
|
8597
8663
|
// …and so is the Enter: the key is sent only while the reading is still within it.
|
|
8598
8664
|
steerEnterDeadline = deadline;
|
|
8599
8665
|
}
|
|
@@ -8665,6 +8731,9 @@ export class Daemon extends EventEmitter {
|
|
|
8665
8731
|
if (after === "steer" || after === "queue" || after === "idle") {
|
|
8666
8732
|
const probe = await withinMs(this.probeBlockingDialog(), deadline - performance.now());
|
|
8667
8733
|
if (probe?.state === "clear" && performance.now() <= deadline && steerCurrent() && this.getWindowId() === windowId) {
|
|
8734
|
+
if (humanComposerOwned)
|
|
8735
|
+
signature.humanPositive?.(after === "steer" ? "composer-empty-steer"
|
|
8736
|
+
: after === "queue" ? "composer-empty-queue" : "composer-empty-idle");
|
|
8668
8737
|
verdict.phase = "steer-accepted";
|
|
8669
8738
|
verdict.proof = `steer-composer-${after}`;
|
|
8670
8739
|
if (status)
|
|
@@ -9251,8 +9320,11 @@ export class Daemon extends EventEmitter {
|
|
|
9251
9320
|
// the input row is proof in itself — which also means a momentary
|
|
9252
9321
|
// failure to read the pane BEFORE pasting cannot turn a delivered
|
|
9253
9322
|
// message into a re-paste.
|
|
9254
|
-
if (signature.unique && after.payload > 0)
|
|
9323
|
+
if (signature.unique && after.payload > 0) {
|
|
9324
|
+
if (after.inputReadable)
|
|
9325
|
+
signature.humanPositive?.("unique-id-pane");
|
|
9255
9326
|
return this.submittedProof();
|
|
9327
|
+
}
|
|
9256
9328
|
if (unattributedResidue)
|
|
9257
9329
|
return "unverifiable";
|
|
9258
9330
|
// 3. Otherwise the evidence must be NEW relative to the pane as it was
|
|
@@ -9389,7 +9461,7 @@ export class Daemon extends EventEmitter {
|
|
|
9389
9461
|
* paste that never landed. System pastes carry no envelope and fall back to
|
|
9390
9462
|
* the body signature, which the baseline comparison still ties to this paste.
|
|
9391
9463
|
*/
|
|
9392
|
-
submissionSignature(formatted, trustedId) {
|
|
9464
|
+
submissionSignature(formatted, trustedId, verification = false) {
|
|
9393
9465
|
// The id must come from the message's own metadata, never from scanning the
|
|
9394
9466
|
// rendered text: agents and users discuss message ids in the body all the
|
|
9395
9467
|
// time ("check message_id: abc"), and a body-scanned value would match an
|
|
@@ -9398,7 +9470,7 @@ export class Daemon extends EventEmitter {
|
|
|
9398
9470
|
// the handoff metadata identifies THIS delivery.
|
|
9399
9471
|
const id = trustedId?.trim();
|
|
9400
9472
|
if (id)
|
|
9401
|
-
return { value:
|
|
9473
|
+
return { value: (verification ? "agend-human-id:" : "message_id:") + id, unique: true };
|
|
9402
9474
|
return { value: pastedTextSignature(formatted), unique: false };
|
|
9403
9475
|
}
|
|
9404
9476
|
/**
|