@parall/parel-channel 1.52.0 → 1.52.1
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/channel-prompt.d.ts +2 -0
- package/dist/channel-prompt.d.ts.map +1 -1
- package/dist/channel-prompt.js +2 -0
- package/dist/delivery.d.ts.map +1 -1
- package/dist/delivery.js +12 -6
- package/dist/inbound.d.ts.map +1 -1
- package/dist/inbound.js +22 -1
- package/dist/session.d.ts +17 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +43 -0
- package/package.json +1 -1
- package/src/channel-prompt.ts +3 -0
- package/src/delivery.ts +12 -5
- package/src/inbound.ts +25 -0
- package/src/session.ts +44 -0
package/dist/channel-prompt.d.ts
CHANGED
|
@@ -43,6 +43,8 @@ export interface ChatPromptArgs {
|
|
|
43
43
|
text: string;
|
|
44
44
|
/** Message attachments — rendered as reference lines (agent fetches via CLI). */
|
|
45
45
|
attachments?: PromptAttachment[];
|
|
46
|
+
/** message.hints.no_reply — rendered as the same [Hint: no_reply] line agent-core injects. */
|
|
47
|
+
noReply?: boolean;
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
50
|
* Frame a Parall chat message with its conversation of origin. Kept to a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-prompt.d.ts","sourceRoot":"","sources":["../src/channel-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"channel-prompt.d.ts","sourceRoot":"","sources":["../src/channel-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AASH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,8FAA8F;IAC9F,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAmBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAW5D;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAkBlE"}
|
package/dist/channel-prompt.js
CHANGED
|
@@ -63,6 +63,8 @@ export function buildChatPrompt(args) {
|
|
|
63
63
|
lines.push(`[From: ${sanitizeMeta(args.senderId)}]`);
|
|
64
64
|
if (args.threadRootId)
|
|
65
65
|
lines.push(`[Thread: ${sanitizeMeta(args.threadRootId)}]`);
|
|
66
|
+
if (args.noReply)
|
|
67
|
+
lines.push(`[Hint: no_reply]`);
|
|
66
68
|
lines.push(...attachmentLines(args.attachments));
|
|
67
69
|
lines.push('', args.text);
|
|
68
70
|
return lines.join('\n');
|
package/dist/delivery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAc5F;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,eAAe,EACzB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAsF5B"}
|
package/dist/delivery.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { childRefKnown } from './fork.js';
|
|
2
|
-
import { completeFencedEnvelopes, createSuppressedTextStep, ensureChildSession, ensureSession, invalidateChildSession, invalidateSession, patchSession, TURN_ENVELOPE_KEY, } from './session.js';
|
|
2
|
+
import { completeFencedEnvelopes, createSuppressedTextStep, ensureChildSession, ensureSession, invalidateChildSession, invalidateSession, patchSession, TURN_ENVELOPE_KEY, turnEventsObserved, } from './session.js';
|
|
3
3
|
/**
|
|
4
4
|
* Turn-end delivery — which, under the platform reply contract, delivers
|
|
5
5
|
* NOTHING to the chat.
|
|
@@ -67,16 +67,22 @@ export async function buildParallDelivery(delivery, ctx) {
|
|
|
67
67
|
// (the npm ^1 fallback path): there turn events never arrive and, with
|
|
68
68
|
// ack-on-emit gone, this hook is the only terminal ledger path — without it
|
|
69
69
|
// a delivered turn's row would stay live and re-emit on every TTL sweep.
|
|
70
|
-
// Under the normal artifact path both hooks run; the fence makes the
|
|
71
|
-
// overlap free (whichever completes first consumes it, the other skips).
|
|
72
70
|
// Fork-handled envelopes never wrote a fence, so this is a no-op for them
|
|
73
71
|
// (their ack machinery owns the ledger there).
|
|
74
|
-
//
|
|
75
|
-
//
|
|
72
|
+
// On an OBSERVING binding this close must stand down (the turn-observed
|
|
73
|
+
// latch): the per-envelope fence makes the overlap idempotent, but not
|
|
74
|
+
// batch-safe — deliver runs before turn_completed, so closing the
|
|
75
|
+
// trigger's envelope here consumes its fence and splits the folded turn's
|
|
76
|
+
// [trigger, sibling] batch; the sibling's complete then finds no covering
|
|
77
|
+
// reply and sweeps to no_action (R-06). Cost of standing down: if the
|
|
78
|
+
// observing binding's turn_completed is lost, the fence heals via the TTL
|
|
79
|
+
// re-emit — the same recovery every lost close already relies on.
|
|
80
|
+
// Known bound of the no-observe fallback: the replyRoute snapshot carries
|
|
81
|
+
// ONE envelope id, so a folded turn's OTHER sources heal only via the TTL
|
|
76
82
|
// re-emit here. Reaching that state needs a binding with injectInFlight
|
|
77
83
|
// ON and observe OFF — the platform template always sets both together,
|
|
78
84
|
// so it exists only on a hand-crafted binding.
|
|
79
|
-
if (route.envelopeId) {
|
|
85
|
+
if (route.envelopeId && !(await turnEventsObserved(ctx))) {
|
|
80
86
|
await completeFencedEnvelopes(ctx, [route.envelopeId], 'ok');
|
|
81
87
|
}
|
|
82
88
|
const subject = route.chatId ?? route.conversationId;
|
package/dist/inbound.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../src/inbound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACf,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../src/inbound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACf,MAAM,mBAAmB,CAAC;AA0H3B,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAWrF;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAE5B;AAoDD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACtB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAE5B;AAsCD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAElF;AAkoBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B"}
|
package/dist/inbound.js
CHANGED
|
@@ -4,7 +4,7 @@ import { armCatchUpEffect, beginCatchUp, CATCHUP_TIMER_KEY, deferToCatchUp, runC
|
|
|
4
4
|
import { parallApiUrl, parallOrgId, requireAgk, sandboxEnabled } from './connect.js';
|
|
5
5
|
import { effectsForChannelDispatch, PROVIDER_LOOKUP_TIMEOUT_MS } from './inbound-channel.js';
|
|
6
6
|
import { buildForkScopePrefix, bumpForkRetryAttempts, childConfirmed, childRefKnown, clearForkState, clearForkSubject, FORK_RETRY_DELAY_MS, FORK_RETRY_PREFIX, handleChildSpawnFailed, MAX_SPAWN_VERIFY_ATTEMPTS, noteMainTurnEvent, planForkDecision, recordSpawn, settleChildEvent, trackFollowUpAck, } from './fork.js';
|
|
7
|
-
import { ackByIdEffect, claimDispatchSource, completeFencedEnvelopes, completeSourceEffect, conversationKey, createErrorStep, createInputStep, createTraceStep, createTypedInputStep, EMIT_TTL_MS, ensureChildSession, ensureSession, invalidateChildSession, invalidateSession, nextEnvelopeId, patchSession, readActiveEmit, reportingEnabled, REQUEST_TIMEOUT_MS, TURN_ENVELOPE_KEY, writeActiveEmit, } from './session.js';
|
|
7
|
+
import { ackByIdEffect, claimDispatchSource, completeFencedEnvelopes, completeSourceEffect, conversationKey, createErrorStep, createInputStep, createTraceStep, createTypedInputStep, EMIT_TTL_MS, ensureChildSession, ensureSession, invalidateChildSession, invalidateSession, nextEnvelopeId, noteTurnEventsObserved, patchSession, readActiveEmit, reportingEnabled, REQUEST_TIMEOUT_MS, TURN_ENVELOPE_KEY, writeActiveEmit, } from './session.js';
|
|
8
8
|
import { isTypedEventType, planTypedDispatch } from './typed-dispatch.js';
|
|
9
9
|
/**
|
|
10
10
|
* Connection-keyed hook serialization — a v1-host compatibility shim. The v1
|
|
@@ -255,6 +255,10 @@ async function effectsForDispatch(data, ctx) {
|
|
|
255
255
|
// prefetch could never persist its cache row.
|
|
256
256
|
const chatName = await cachedChatName(ctx, chatId);
|
|
257
257
|
const senderId = msg?.sender_id ? String(msg.sender_id) : undefined;
|
|
258
|
+
// Structured no-reply hint (R-10): render the same [Hint: no_reply] line
|
|
259
|
+
// agent-core injects for the standard runtimes, instead of silently
|
|
260
|
+
// dropping the hint and betting on the body text.
|
|
261
|
+
const noReply = msg?.hints?.no_reply === true;
|
|
258
262
|
const framedText = buildChatPrompt({
|
|
259
263
|
chatId,
|
|
260
264
|
chatName,
|
|
@@ -262,6 +266,7 @@ async function effectsForDispatch(data, ctx) {
|
|
|
262
266
|
threadRootId,
|
|
263
267
|
text,
|
|
264
268
|
attachments,
|
|
269
|
+
noReply,
|
|
265
270
|
});
|
|
266
271
|
// Deterministic source-derived envelope id (+ attempt suffix on re-emits):
|
|
267
272
|
// complete-sources strips the suffix back to the source, and every re-emit
|
|
@@ -298,6 +303,10 @@ async function effectsForDispatch(data, ctx) {
|
|
|
298
303
|
PRLL_DISPATCH_SOURCE_TYPE: sourceType,
|
|
299
304
|
PRLL_DISPATCH_SOURCE_ID: sourceId,
|
|
300
305
|
...(threadRootId ? { PRLL_THREAD_ROOT_ID: threadRootId } : {}),
|
|
306
|
+
// Same env contract the standard runtimes' context file carries — the
|
|
307
|
+
// CLI already parses PRLL_NO_REPLY; today it is informational (prompt
|
|
308
|
+
// hint above does the suppression), but the context must not lie.
|
|
309
|
+
...(noReply ? { PRLL_NO_REPLY: '1' } : {}),
|
|
301
310
|
...(capabilityEnv ?? {}),
|
|
302
311
|
};
|
|
303
312
|
const envelope = {
|
|
@@ -751,6 +760,18 @@ export function handleParallAgentEvent(event, ctx) {
|
|
|
751
760
|
return serialized(ctx, () => handleParallAgentEventInner(event, ctx));
|
|
752
761
|
}
|
|
753
762
|
async function handleParallAgentEventInner(event, ctx) {
|
|
763
|
+
// Turn-observed latch (delivery.ts's close gate): ONLY the turn lifecycle
|
|
764
|
+
// proves this binding will get a turn_completed, so the deliver-side
|
|
765
|
+
// redundant close can stand down and let turn_completed settle each folded
|
|
766
|
+
// batch whole. Step events (observe: [steps] without turn) and
|
|
767
|
+
// child_spawn_failed (pushed regardless of observe scopes) reach bindings
|
|
768
|
+
// that never see a turn end — arming on those would suppress their ONLY
|
|
769
|
+
// terminal ledger path, and keep refreshing the suppression.
|
|
770
|
+
if (event.type === 'turn_started' ||
|
|
771
|
+
event.type === 'turn_completed' ||
|
|
772
|
+
event.type === 'turn_failed') {
|
|
773
|
+
await noteTurnEventsObserved(ctx);
|
|
774
|
+
}
|
|
754
775
|
// spawnChildSession's asynchronous error channel — not turn-scoped and
|
|
755
776
|
// pushed regardless of observe scopes. Handle it before anything
|
|
756
777
|
// subject-gated.
|
package/dist/session.d.ts
CHANGED
|
@@ -43,6 +43,23 @@ export declare function reportingEnabled(ctx: ConnectorContext): boolean;
|
|
|
43
43
|
*/
|
|
44
44
|
export declare const TURN_ENVELOPE_KEY = "turnEnvelope:main";
|
|
45
45
|
export declare const SESSION_CACHE_KEY = "session:main";
|
|
46
|
+
/**
|
|
47
|
+
* Turn-observed latch: proof this binding observes the TURN lifecycle, armed
|
|
48
|
+
* by turn_started / turn_completed / turn_failed only. deliver()'s redundant
|
|
49
|
+
* turn-end close exists only for bindings WITHOUT `observe: [turn]` — on an
|
|
50
|
+
* observing binding that close races turn_completed and can consume the
|
|
51
|
+
* trigger's fence FIRST, splitting a folded turn's [trigger, sibling] batch
|
|
52
|
+
* so the sibling's complete finds no covering reply and sweeps to no_action
|
|
53
|
+
* (R-06). Step events and child_spawn_failed must NOT arm it: they reach
|
|
54
|
+
* bindings that never receive a turn end, whose deliver-side close is their
|
|
55
|
+
* only terminal ledger path. The TTL keeps the fallback able to return should
|
|
56
|
+
* a binding's turn observation ever be turned off: a day with zero turn
|
|
57
|
+
* events ages the latch out and the deliver-side close resumes.
|
|
58
|
+
*/
|
|
59
|
+
export declare const TURN_EVENTS_OBSERVED_KEY = "turnEventsObserved:main";
|
|
60
|
+
export declare const TURN_OBSERVED_TTL_MS: number;
|
|
61
|
+
export declare function noteTurnEventsObserved(ctx: ConnectorContext): Promise<void>;
|
|
62
|
+
export declare function turnEventsObserved(ctx: ConnectorContext): Promise<boolean>;
|
|
46
63
|
/**
|
|
47
64
|
* Conversation key for envelope subjects and fork routing. Thread-aware:
|
|
48
65
|
* parel's turn grouping (F2) and mid-turn injection (F3) key on the envelope
|
package/dist/session.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
|
package/dist/session.js
CHANGED
|
@@ -46,6 +46,49 @@ export function reportingEnabled(ctx) {
|
|
|
46
46
|
export const TURN_ENVELOPE_KEY = `turnEnvelope:${MAIN_SESSION_KEY}`;
|
|
47
47
|
export const SESSION_CACHE_KEY = `session:${MAIN_SESSION_KEY}`;
|
|
48
48
|
const childSessionCacheKey = (childRef) => `sessionChild:${childRef}`;
|
|
49
|
+
/**
|
|
50
|
+
* Turn-observed latch: proof this binding observes the TURN lifecycle, armed
|
|
51
|
+
* by turn_started / turn_completed / turn_failed only. deliver()'s redundant
|
|
52
|
+
* turn-end close exists only for bindings WITHOUT `observe: [turn]` — on an
|
|
53
|
+
* observing binding that close races turn_completed and can consume the
|
|
54
|
+
* trigger's fence FIRST, splitting a folded turn's [trigger, sibling] batch
|
|
55
|
+
* so the sibling's complete finds no covering reply and sweeps to no_action
|
|
56
|
+
* (R-06). Step events and child_spawn_failed must NOT arm it: they reach
|
|
57
|
+
* bindings that never receive a turn end, whose deliver-side close is their
|
|
58
|
+
* only terminal ledger path. The TTL keeps the fallback able to return should
|
|
59
|
+
* a binding's turn observation ever be turned off: a day with zero turn
|
|
60
|
+
* events ages the latch out and the deliver-side close resumes.
|
|
61
|
+
*/
|
|
62
|
+
export const TURN_EVENTS_OBSERVED_KEY = `turnEventsObserved:${MAIN_SESSION_KEY}`;
|
|
63
|
+
export const TURN_OBSERVED_TTL_MS = 24 * 60 * 60 * 1000;
|
|
64
|
+
export async function noteTurnEventsObserved(ctx) {
|
|
65
|
+
// A failed write is not propagated: throwing would fail the whole hook and
|
|
66
|
+
// buy a platform replay of an event whose other effects (steps, session
|
|
67
|
+
// patches) already landed, to re-arm a latch the NEXT turn event re-arms
|
|
68
|
+
// anyway — every turn opens with turn_started. The read below is where a
|
|
69
|
+
// store failure actually has to be handled safely.
|
|
70
|
+
await ctx.store?.set(TURN_EVENTS_OBSERVED_KEY, ctx.now()).catch(() => { });
|
|
71
|
+
}
|
|
72
|
+
export async function turnEventsObserved(ctx) {
|
|
73
|
+
// No store at all: nothing was ever armed, so the fallback close is this
|
|
74
|
+
// binding's only terminal path — it must run.
|
|
75
|
+
if (!ctx.store)
|
|
76
|
+
return false;
|
|
77
|
+
let at;
|
|
78
|
+
try {
|
|
79
|
+
at = await ctx.store.get(TURN_EVENTS_OBSERVED_KEY);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// A transient read failure must NOT read as "this binding does not observe
|
|
83
|
+
// turns". That answer runs the deliver-side close, the destructive branch:
|
|
84
|
+
// it consumes the trigger's fence and can split a folded batch, which is
|
|
85
|
+
// R-06 itself. Failing closed costs at most a missed fallback close, which
|
|
86
|
+
// the existing 30min TTL re-emit heals; failing open costs a sibling swept
|
|
87
|
+
// to no_action, which nothing recovers.
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return typeof at === 'number' && ctx.now() - at < TURN_OBSERVED_TTL_MS;
|
|
91
|
+
}
|
|
49
92
|
/**
|
|
50
93
|
* Conversation key for envelope subjects and fork routing. Thread-aware:
|
|
51
94
|
* parel's turn grouping (F2) and mid-turn injection (F3) key on the envelope
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/parel-channel",
|
|
3
|
-
"version": "1.52.
|
|
3
|
+
"version": "1.52.1",
|
|
4
4
|
"description": "Parall channel plugin for the parel runtime — lets a parel agent treat Parall as a managed_ws channel (receive dispatches, reply via the Parall API)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/src/channel-prompt.ts
CHANGED
|
@@ -52,6 +52,8 @@ export interface ChatPromptArgs {
|
|
|
52
52
|
text: string;
|
|
53
53
|
/** Message attachments — rendered as reference lines (agent fetches via CLI). */
|
|
54
54
|
attachments?: PromptAttachment[];
|
|
55
|
+
/** message.hints.no_reply — rendered as the same [Hint: no_reply] line agent-core injects. */
|
|
56
|
+
noReply?: boolean;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/**
|
|
@@ -84,6 +86,7 @@ export function buildChatPrompt(args: ChatPromptArgs): string {
|
|
|
84
86
|
const lines = [`[Chat: ${chatLabel}]`];
|
|
85
87
|
if (args.senderId) lines.push(`[From: ${sanitizeMeta(args.senderId)}]`);
|
|
86
88
|
if (args.threadRootId) lines.push(`[Thread: ${sanitizeMeta(args.threadRootId)}]`);
|
|
89
|
+
if (args.noReply) lines.push(`[Hint: no_reply]`);
|
|
87
90
|
lines.push(...attachmentLines(args.attachments));
|
|
88
91
|
lines.push('', args.text);
|
|
89
92
|
return lines.join('\n');
|
package/src/delivery.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
invalidateSession,
|
|
10
10
|
patchSession,
|
|
11
11
|
TURN_ENVELOPE_KEY,
|
|
12
|
+
turnEventsObserved,
|
|
12
13
|
} from './session.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -89,16 +90,22 @@ export async function buildParallDelivery(
|
|
|
89
90
|
// (the npm ^1 fallback path): there turn events never arrive and, with
|
|
90
91
|
// ack-on-emit gone, this hook is the only terminal ledger path — without it
|
|
91
92
|
// a delivered turn's row would stay live and re-emit on every TTL sweep.
|
|
92
|
-
// Under the normal artifact path both hooks run; the fence makes the
|
|
93
|
-
// overlap free (whichever completes first consumes it, the other skips).
|
|
94
93
|
// Fork-handled envelopes never wrote a fence, so this is a no-op for them
|
|
95
94
|
// (their ack machinery owns the ledger there).
|
|
96
|
-
//
|
|
97
|
-
//
|
|
95
|
+
// On an OBSERVING binding this close must stand down (the turn-observed
|
|
96
|
+
// latch): the per-envelope fence makes the overlap idempotent, but not
|
|
97
|
+
// batch-safe — deliver runs before turn_completed, so closing the
|
|
98
|
+
// trigger's envelope here consumes its fence and splits the folded turn's
|
|
99
|
+
// [trigger, sibling] batch; the sibling's complete then finds no covering
|
|
100
|
+
// reply and sweeps to no_action (R-06). Cost of standing down: if the
|
|
101
|
+
// observing binding's turn_completed is lost, the fence heals via the TTL
|
|
102
|
+
// re-emit — the same recovery every lost close already relies on.
|
|
103
|
+
// Known bound of the no-observe fallback: the replyRoute snapshot carries
|
|
104
|
+
// ONE envelope id, so a folded turn's OTHER sources heal only via the TTL
|
|
98
105
|
// re-emit here. Reaching that state needs a binding with injectInFlight
|
|
99
106
|
// ON and observe OFF — the platform template always sets both together,
|
|
100
107
|
// so it exists only on a hand-crafted binding.
|
|
101
|
-
if (route.envelopeId) {
|
|
108
|
+
if (route.envelopeId && !(await turnEventsObserved(ctx))) {
|
|
102
109
|
await completeFencedEnvelopes(ctx, [route.envelopeId], 'ok');
|
|
103
110
|
}
|
|
104
111
|
|
package/src/inbound.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
invalidateChildSession,
|
|
50
50
|
invalidateSession,
|
|
51
51
|
nextEnvelopeId,
|
|
52
|
+
noteTurnEventsObserved,
|
|
52
53
|
patchSession,
|
|
53
54
|
readActiveEmit,
|
|
54
55
|
reportingEnabled,
|
|
@@ -355,6 +356,7 @@ async function effectsForDispatch(
|
|
|
355
356
|
content?: { text?: string };
|
|
356
357
|
thread_root_id?: string;
|
|
357
358
|
sender_id?: string;
|
|
359
|
+
hints?: { no_reply?: boolean };
|
|
358
360
|
attachments?: Array<{
|
|
359
361
|
id?: string;
|
|
360
362
|
file_name?: string;
|
|
@@ -392,6 +394,10 @@ async function effectsForDispatch(
|
|
|
392
394
|
// prefetch could never persist its cache row.
|
|
393
395
|
const chatName = await cachedChatName(ctx, chatId);
|
|
394
396
|
const senderId = msg?.sender_id ? String(msg.sender_id) : undefined;
|
|
397
|
+
// Structured no-reply hint (R-10): render the same [Hint: no_reply] line
|
|
398
|
+
// agent-core injects for the standard runtimes, instead of silently
|
|
399
|
+
// dropping the hint and betting on the body text.
|
|
400
|
+
const noReply = msg?.hints?.no_reply === true;
|
|
395
401
|
const framedText = buildChatPrompt({
|
|
396
402
|
chatId,
|
|
397
403
|
chatName,
|
|
@@ -399,6 +405,7 @@ async function effectsForDispatch(
|
|
|
399
405
|
threadRootId,
|
|
400
406
|
text,
|
|
401
407
|
attachments,
|
|
408
|
+
noReply,
|
|
402
409
|
});
|
|
403
410
|
// Deterministic source-derived envelope id (+ attempt suffix on re-emits):
|
|
404
411
|
// complete-sources strips the suffix back to the source, and every re-emit
|
|
@@ -437,6 +444,10 @@ async function effectsForDispatch(
|
|
|
437
444
|
PRLL_DISPATCH_SOURCE_TYPE: sourceType,
|
|
438
445
|
PRLL_DISPATCH_SOURCE_ID: sourceId,
|
|
439
446
|
...(threadRootId ? { PRLL_THREAD_ROOT_ID: threadRootId } : {}),
|
|
447
|
+
// Same env contract the standard runtimes' context file carries — the
|
|
448
|
+
// CLI already parses PRLL_NO_REPLY; today it is informational (prompt
|
|
449
|
+
// hint above does the suppression), but the context must not lie.
|
|
450
|
+
...(noReply ? { PRLL_NO_REPLY: '1' } : {}),
|
|
440
451
|
...(capabilityEnv ?? {}),
|
|
441
452
|
};
|
|
442
453
|
|
|
@@ -917,6 +928,20 @@ async function handleParallAgentEventInner(
|
|
|
917
928
|
event: AgentEvent,
|
|
918
929
|
ctx: ConnectorContext,
|
|
919
930
|
): Promise<AgentEventEffect[]> {
|
|
931
|
+
// Turn-observed latch (delivery.ts's close gate): ONLY the turn lifecycle
|
|
932
|
+
// proves this binding will get a turn_completed, so the deliver-side
|
|
933
|
+
// redundant close can stand down and let turn_completed settle each folded
|
|
934
|
+
// batch whole. Step events (observe: [steps] without turn) and
|
|
935
|
+
// child_spawn_failed (pushed regardless of observe scopes) reach bindings
|
|
936
|
+
// that never see a turn end — arming on those would suppress their ONLY
|
|
937
|
+
// terminal ledger path, and keep refreshing the suppression.
|
|
938
|
+
if (
|
|
939
|
+
event.type === 'turn_started' ||
|
|
940
|
+
event.type === 'turn_completed' ||
|
|
941
|
+
event.type === 'turn_failed'
|
|
942
|
+
) {
|
|
943
|
+
await noteTurnEventsObserved(ctx);
|
|
944
|
+
}
|
|
920
945
|
// spawnChildSession's asynchronous error channel — not turn-scoped and
|
|
921
946
|
// pushed regardless of observe scopes. Handle it before anything
|
|
922
947
|
// subject-gated.
|
package/src/session.ts
CHANGED
|
@@ -53,6 +53,50 @@ export const TURN_ENVELOPE_KEY = `turnEnvelope:${MAIN_SESSION_KEY}`;
|
|
|
53
53
|
export const SESSION_CACHE_KEY = `session:${MAIN_SESSION_KEY}`;
|
|
54
54
|
const childSessionCacheKey = (childRef: string) => `sessionChild:${childRef}`;
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Turn-observed latch: proof this binding observes the TURN lifecycle, armed
|
|
58
|
+
* by turn_started / turn_completed / turn_failed only. deliver()'s redundant
|
|
59
|
+
* turn-end close exists only for bindings WITHOUT `observe: [turn]` — on an
|
|
60
|
+
* observing binding that close races turn_completed and can consume the
|
|
61
|
+
* trigger's fence FIRST, splitting a folded turn's [trigger, sibling] batch
|
|
62
|
+
* so the sibling's complete finds no covering reply and sweeps to no_action
|
|
63
|
+
* (R-06). Step events and child_spawn_failed must NOT arm it: they reach
|
|
64
|
+
* bindings that never receive a turn end, whose deliver-side close is their
|
|
65
|
+
* only terminal ledger path. The TTL keeps the fallback able to return should
|
|
66
|
+
* a binding's turn observation ever be turned off: a day with zero turn
|
|
67
|
+
* events ages the latch out and the deliver-side close resumes.
|
|
68
|
+
*/
|
|
69
|
+
export const TURN_EVENTS_OBSERVED_KEY = `turnEventsObserved:${MAIN_SESSION_KEY}`;
|
|
70
|
+
export const TURN_OBSERVED_TTL_MS = 24 * 60 * 60 * 1000;
|
|
71
|
+
|
|
72
|
+
export async function noteTurnEventsObserved(ctx: ConnectorContext): Promise<void> {
|
|
73
|
+
// A failed write is not propagated: throwing would fail the whole hook and
|
|
74
|
+
// buy a platform replay of an event whose other effects (steps, session
|
|
75
|
+
// patches) already landed, to re-arm a latch the NEXT turn event re-arms
|
|
76
|
+
// anyway — every turn opens with turn_started. The read below is where a
|
|
77
|
+
// store failure actually has to be handled safely.
|
|
78
|
+
await ctx.store?.set(TURN_EVENTS_OBSERVED_KEY, ctx.now()).catch(() => {});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function turnEventsObserved(ctx: ConnectorContext): Promise<boolean> {
|
|
82
|
+
// No store at all: nothing was ever armed, so the fallback close is this
|
|
83
|
+
// binding's only terminal path — it must run.
|
|
84
|
+
if (!ctx.store) return false;
|
|
85
|
+
let at: unknown;
|
|
86
|
+
try {
|
|
87
|
+
at = await ctx.store.get(TURN_EVENTS_OBSERVED_KEY);
|
|
88
|
+
} catch {
|
|
89
|
+
// A transient read failure must NOT read as "this binding does not observe
|
|
90
|
+
// turns". That answer runs the deliver-side close, the destructive branch:
|
|
91
|
+
// it consumes the trigger's fence and can split a folded batch, which is
|
|
92
|
+
// R-06 itself. Failing closed costs at most a missed fallback close, which
|
|
93
|
+
// the existing 30min TTL re-emit heals; failing open costs a sibling swept
|
|
94
|
+
// to no_action, which nothing recovers.
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return typeof at === 'number' && ctx.now() - at < TURN_OBSERVED_TTL_MS;
|
|
98
|
+
}
|
|
99
|
+
|
|
56
100
|
/**
|
|
57
101
|
* Conversation key for envelope subjects and fork routing. Thread-aware:
|
|
58
102
|
* parel's turn grouping (F2) and mid-turn injection (F3) key on the envelope
|