@parall/parel-channel 1.42.1 → 1.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/channel-capability.d.ts +29 -0
- package/dist/channel-capability.d.ts.map +1 -0
- package/dist/channel-capability.js +81 -0
- package/dist/channel-prompt.d.ts +16 -9
- package/dist/channel-prompt.d.ts.map +1 -1
- package/dist/channel-prompt.js +25 -19
- package/dist/delivery.js +1 -1
- package/dist/fork.d.ts +8 -4
- package/dist/fork.d.ts.map +1 -1
- package/dist/inbound-channel.d.ts.map +1 -1
- package/dist/inbound-channel.js +19 -14
- package/dist/inbound.d.ts.map +1 -1
- package/dist/inbound.js +65 -43
- package/dist/session.d.ts +43 -19
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +75 -36
- package/package.json +2 -2
- package/src/channel-capability.ts +121 -0
- package/src/channel-prompt.ts +33 -20
- package/src/delivery.ts +1 -1
- package/src/fork.ts +6 -7
- package/src/inbound-channel.ts +21 -15
- package/src/inbound.ts +75 -52
- package/src/session.ts +81 -34
- package/dist/parel-sdk-compat.d.ts +0 -63
- package/dist/parel-sdk-compat.d.ts.map +0 -1
- package/dist/parel-sdk-compat.js +0 -16
- package/src/parel-sdk-compat.ts +0 -77
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel-capability credential injection — the parel-native form of the
|
|
3
|
+
* feishu-cli broker (multi-channel-architecture-design §8.3).
|
|
4
|
+
*
|
|
5
|
+
* Bridges deliver the capability as a PATH pointer shim that mints a fresh
|
|
6
|
+
* short-lived tenant token per lark-cli invocation. In parel we control
|
|
7
|
+
* neither the sandbox PATH nor a bridge process — but we DO control the
|
|
8
|
+
* per-turn invocation context, which sandbox-e2b flattens into every exec's
|
|
9
|
+
* env. So the connector mints at emit time and injects the official
|
|
10
|
+
* LARKSUITE_CLI_* env set: bare `lark-cli` works ambiently for the whole
|
|
11
|
+
* turn (turn length ≪ token TTL ~2h), the app secret never leaves the
|
|
12
|
+
* platform, and revocation bites on the next emit's mint (403 → no env).
|
|
13
|
+
*
|
|
14
|
+
* The mint endpoint IS the gate (the platform re-evaluates connection state
|
|
15
|
+
* + flag on every request — the no-drift invariant), so the connector needs
|
|
16
|
+
* no declaration-plane fetch: a 403 is the revocation signal and is
|
|
17
|
+
* negative-cached briefly to keep a revoked grant from adding one 403 per
|
|
18
|
+
* turn.
|
|
19
|
+
*/
|
|
20
|
+
import type { ConnectorContext } from '@parel/plugin-sdk';
|
|
21
|
+
/** Test hook: the cache is module-level state, so suites reset it between cases. */
|
|
22
|
+
export declare function resetChannelCapabilityCache(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the feishu-cli env set to spread into an envelope's invocation
|
|
25
|
+
* context, or null when the capability is not granted (or the mint degraded
|
|
26
|
+
* — a transient platform error must never block a dispatch emit).
|
|
27
|
+
*/
|
|
28
|
+
export declare function channelCapabilityEnv(ctx: ConnectorContext): Promise<Record<string, string> | null>;
|
|
29
|
+
//# sourceMappingURL=channel-capability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-capability.d.ts","sourceRoot":"","sources":["../src/channel-capability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAkC1D,oFAAoF;AACpF,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAcD;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CA2CxC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { parallApiUrl, parallOrgId, requireAgk } from './connect.js';
|
|
2
|
+
const MINT_TIMEOUT_MS = 5_000;
|
|
3
|
+
// Re-mint this long before the token expires so an env set injected at turn
|
|
4
|
+
// start stays valid through a long turn's tail.
|
|
5
|
+
const EXPIRY_SKEW_MS = 10 * 60 * 1000;
|
|
6
|
+
// After a 403 (no grant / revoked / flag off), skip minting for this long —
|
|
7
|
+
// revocation latency stays bounded while per-turn 403 noise does not.
|
|
8
|
+
const DENIED_TTL_MS = 60 * 1000;
|
|
9
|
+
// A GRANTED result is re-validated on the same cadence: the mint endpoint IS
|
|
10
|
+
// the revocation gate, so a positive cache lasting the token TTL (~2h) would
|
|
11
|
+
// extend revocation latency to hours. Re-minting is cheap (the platform
|
|
12
|
+
// Redis-caches the exchanged token and re-evaluates the gate per request);
|
|
13
|
+
// this cache only absorbs bursty emits within a conversation.
|
|
14
|
+
const GRANTED_TTL_MS = 60 * 1000;
|
|
15
|
+
// Per-connection cache. The plugin isolate is per-connection, so a plain
|
|
16
|
+
// module map keyed by connectionId is effectively single-entry — the key
|
|
17
|
+
// guards against isolate reuse across connections.
|
|
18
|
+
const cache = new Map();
|
|
19
|
+
/** Test hook: the cache is module-level state, so suites reset it between cases. */
|
|
20
|
+
export function resetChannelCapabilityCache() {
|
|
21
|
+
cache.clear();
|
|
22
|
+
}
|
|
23
|
+
function envFromMint(minted) {
|
|
24
|
+
return {
|
|
25
|
+
LARKSUITE_CLI_APP_ID: minted.app_id,
|
|
26
|
+
LARKSUITE_CLI_BRAND: minted.brand || 'feishu',
|
|
27
|
+
LARKSUITE_CLI_TENANT_ACCESS_TOKEN: minted.token,
|
|
28
|
+
// Bot lock: the credential is the app's, not a human's — mirror the
|
|
29
|
+
// bridge shim's injection exactly (agent-core channel-exec).
|
|
30
|
+
LARKSUITE_CLI_DEFAULT_AS: 'bot',
|
|
31
|
+
LARKSUITE_CLI_STRICT_MODE: 'bot',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns the feishu-cli env set to spread into an envelope's invocation
|
|
36
|
+
* context, or null when the capability is not granted (or the mint degraded
|
|
37
|
+
* — a transient platform error must never block a dispatch emit).
|
|
38
|
+
*/
|
|
39
|
+
export async function channelCapabilityEnv(ctx) {
|
|
40
|
+
const key = ctx.connectionId;
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
const hit = cache.get(key);
|
|
43
|
+
if (hit && hit.validUntil > now)
|
|
44
|
+
return hit.env;
|
|
45
|
+
try {
|
|
46
|
+
const res = await fetch(`${parallApiUrl(ctx)}/api/v1/orgs/${encodeURIComponent(parallOrgId(ctx))}/agents/me/channel-token`, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: {
|
|
49
|
+
authorization: `Bearer ${requireAgk(ctx)}`,
|
|
50
|
+
'content-type': 'application/json',
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({ channel_type: 'feishu' }),
|
|
53
|
+
signal: AbortSignal.timeout(MINT_TIMEOUT_MS),
|
|
54
|
+
});
|
|
55
|
+
if (res.status === 403 || res.status === 404) {
|
|
56
|
+
// No grant (never had it / revoked / flag off) — or an old server
|
|
57
|
+
// without the endpoint. Same agent-facing outcome: no capability.
|
|
58
|
+
cache.set(key, { env: null, validUntil: now + DENIED_TTL_MS });
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
// Transient (5xx/502-mint-upstream): don't cache, don't block the emit.
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const minted = (await res.json());
|
|
66
|
+
if (!minted?.token || !minted.app_id)
|
|
67
|
+
return null;
|
|
68
|
+
const env = envFromMint(minted);
|
|
69
|
+
const expiresAt = Date.parse(minted.expires_at);
|
|
70
|
+
// Bounded by BOTH the revalidation cadence (revocation latency) and the
|
|
71
|
+
// token's own remaining life (an injected env must outlive the turn).
|
|
72
|
+
const tokenBound = Number.isFinite(expiresAt) ? expiresAt - EXPIRY_SKEW_MS : now;
|
|
73
|
+
const validUntil = Math.max(now + 5_000, Math.min(now + GRANTED_TTL_MS, tokenBound));
|
|
74
|
+
cache.set(key, { env, validUntil });
|
|
75
|
+
return env;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Network failure — degrade to "no env this turn"; the next emit retries.
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
package/dist/channel-prompt.d.ts
CHANGED
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
*
|
|
17
17
|
* - The audience is OUTSIDE Parall — no prll:// refs, no internal cards or
|
|
18
18
|
* attachment links in the reply (dead links there).
|
|
19
|
-
* -
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
19
|
+
* - Single-path reply (multi-channel-architecture-design §6): with the
|
|
20
|
+
* feishu-cli capability granted, the reply goes out through the official
|
|
21
|
+
* lark-cli — auth rides the turn's invocation context env
|
|
22
|
+
* (channel-capability.ts), so bare `lark-cli` works in the sandbox.
|
|
23
|
+
* Without the grant there is no outbound path and the hint says so (the
|
|
24
|
+
* provider clip path is retired).
|
|
25
25
|
* - When the inbound external message id is known, offer the in-thread reply
|
|
26
|
-
* form
|
|
27
|
-
*
|
|
26
|
+
* form so threaded conversations aren't nudged toward new top-level
|
|
27
|
+
* messages.
|
|
28
28
|
*/
|
|
29
29
|
export interface PromptAttachment {
|
|
30
30
|
id: string;
|
|
@@ -52,7 +52,7 @@ export interface ChatPromptArgs {
|
|
|
52
52
|
*/
|
|
53
53
|
export declare function buildChatPrompt(args: ChatPromptArgs): string;
|
|
54
54
|
export interface ChannelPromptArgs {
|
|
55
|
-
/** Provider name (
|
|
55
|
+
/** Provider name (e.g. "feishu"); undefined when unresolved. */
|
|
56
56
|
provider?: string;
|
|
57
57
|
/** Conversation shape as recorded on the ChannelConversation (dm/group). */
|
|
58
58
|
conversationType?: string;
|
|
@@ -61,6 +61,13 @@ export interface ChannelPromptArgs {
|
|
|
61
61
|
senderName: string;
|
|
62
62
|
/** The inbound message text (raw, un-framed). */
|
|
63
63
|
text: string;
|
|
64
|
+
/**
|
|
65
|
+
* Live capability grant: the turn's invocation context carries the minted
|
|
66
|
+
* LARKSUITE_CLI_* env, so bare `lark-cli` works in the sandbox and is THE
|
|
67
|
+
* reply path. False → outbound is disabled for this org (no grant); the
|
|
68
|
+
* hint says so instead of pointing at the retired provider clip.
|
|
69
|
+
*/
|
|
70
|
+
cliCapable?: boolean;
|
|
64
71
|
}
|
|
65
72
|
export declare function buildChannelPrompt(args: ChannelPromptArgs): string;
|
|
66
73
|
//# sourceMappingURL=channel-prompt.d.ts.map
|
|
@@ -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;CAClC;AAmBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAU5D;AAED,MAAM,WAAW,iBAAiB;IAChC,
|
|
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;CAClC;AAmBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAU5D;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
|
@@ -16,15 +16,15 @@
|
|
|
16
16
|
*
|
|
17
17
|
* - The audience is OUTSIDE Parall — no prll:// refs, no internal cards or
|
|
18
18
|
* attachment links in the reply (dead links there).
|
|
19
|
-
* -
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
19
|
+
* - Single-path reply (multi-channel-architecture-design §6): with the
|
|
20
|
+
* feishu-cli capability granted, the reply goes out through the official
|
|
21
|
+
* lark-cli — auth rides the turn's invocation context env
|
|
22
|
+
* (channel-capability.ts), so bare `lark-cli` works in the sandbox.
|
|
23
|
+
* Without the grant there is no outbound path and the hint says so (the
|
|
24
|
+
* provider clip path is retired).
|
|
25
25
|
* - When the inbound external message id is known, offer the in-thread reply
|
|
26
|
-
* form
|
|
27
|
-
*
|
|
26
|
+
* form so threaded conversations aren't nudged toward new top-level
|
|
27
|
+
* messages.
|
|
28
28
|
*/
|
|
29
29
|
function sanitizeMeta(value) {
|
|
30
30
|
return value
|
|
@@ -85,16 +85,22 @@ export function buildChannelPrompt(args) {
|
|
|
85
85
|
return lines.join('\n') + buildChannelReplyHint(args);
|
|
86
86
|
}
|
|
87
87
|
function buildChannelReplyHint(args) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
if (args.
|
|
96
|
-
const
|
|
97
|
-
|
|
88
|
+
// Single-path routing (multi-channel-architecture-design §6/§8.3): with
|
|
89
|
+
// the feishu-cli grant, the minted LARKSUITE_CLI_* env rides this turn's
|
|
90
|
+
// invocation context, so the official lark-cli works ambiently in the
|
|
91
|
+
// sandbox and is THE reply path. Without the grant there is no outbound
|
|
92
|
+
// path — say so instead of pointing at the retired provider clip.
|
|
93
|
+
// cliCapable alone decides: the mint is feishu-specific, so a successful
|
|
94
|
+
// grant implies Feishu even when the cosmetic provider-label lookup failed.
|
|
95
|
+
if (args.cliCapable) {
|
|
96
|
+
const convRef = args.externalConversationId
|
|
97
|
+
? `chat_id "${args.externalConversationId}"`
|
|
98
|
+
: 'the conversation id named in this event';
|
|
99
|
+
const threadAlt = args.externalMessageId
|
|
100
|
+
? ` To reply threaded to this specific message, reference message_id "${args.externalMessageId}".`
|
|
101
|
+
: '';
|
|
102
|
+
return `\n<system-reminder>To reply, use the official Feishu CLI in your sandbox: send a message to ${convRef} with \`lark-cli im\` (see \`lark-cli im --help\` for send syntax; auth is provisioned in your environment — if lark-cli is missing, install it once with \`npm i -g @larksuite/cli\`).${threadAlt} lark-cli is the ONLY outbound path — your plain text output is NOT delivered to the external conversation.</system-reminder>`;
|
|
98
103
|
}
|
|
99
|
-
|
|
104
|
+
const platform = args.provider ?? 'the external platform';
|
|
105
|
+
return `\n<system-reminder>This message arrived from ${platform}, but outbound replies are currently disabled for this org (no channel capability granted). Do NOT attempt to reply on the external platform. If action is needed, surface it inside Parall (\`parall messages send\` / \`parall dm\`). Your plain text output is not delivered anywhere.</system-reminder>`;
|
|
100
106
|
}
|
package/dist/delivery.js
CHANGED
|
@@ -77,7 +77,7 @@ export async function buildParallDelivery(delivery, ctx) {
|
|
|
77
77
|
// ON and observe OFF — the platform template always sets both together,
|
|
78
78
|
// so it exists only on a hand-crafted binding.
|
|
79
79
|
if (route.envelopeId) {
|
|
80
|
-
await completeFencedEnvelopes(ctx, [route.envelopeId]);
|
|
80
|
+
await completeFencedEnvelopes(ctx, [route.envelopeId], 'ok');
|
|
81
81
|
}
|
|
82
82
|
const subject = route.chatId ?? route.conversationId;
|
|
83
83
|
if (subject) {
|
package/dist/fork.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { ConnectorContext } from '@parel/plugin-sdk';
|
|
2
|
-
|
|
1
|
+
import type { AgentEvent, ConnectorContext } from '@parel/plugin-sdk';
|
|
2
|
+
/** The child-spawn failure variant of the sdk's AgentEvent union. */
|
|
3
|
+
type ChildSpawnFailedEvent = Extract<AgentEvent, {
|
|
4
|
+
type: 'child_spawn_failed';
|
|
5
|
+
}>;
|
|
3
6
|
/** Delay before a failed spawn's messages are re-driven through onTimer. */
|
|
4
7
|
export declare const FORK_RETRY_DELAY_MS = 15000;
|
|
5
8
|
export declare const FORK_RETRY_PREFIX = "forkRetry:";
|
|
@@ -99,12 +102,12 @@ export declare function clearForkState(ctx: ConnectorContext): Promise<string[]>
|
|
|
99
102
|
* Track main-session busyness from the E1 turn stream. Child events (they
|
|
100
103
|
* carry childRef) must NOT feed this — only the main session's turns do.
|
|
101
104
|
*/
|
|
102
|
-
export declare function noteMainTurnEvent(ctx: ConnectorContext, event:
|
|
105
|
+
export declare function noteMainTurnEvent(ctx: ConnectorContext, event: AgentEvent): Promise<void>;
|
|
103
106
|
/**
|
|
104
107
|
* Resolve a child event's bookkeeping row (by its subject mirror) and fire
|
|
105
108
|
* the pending spawn ack exactly once. Returns the ack request when due.
|
|
106
109
|
*/
|
|
107
|
-
export declare function settleChildEvent(ctx: ConnectorContext, event:
|
|
110
|
+
export declare function settleChildEvent(ctx: ConnectorContext, event: AgentEvent): Promise<{
|
|
108
111
|
subject: string;
|
|
109
112
|
pendingAcks: PendingAck[];
|
|
110
113
|
} | null>;
|
|
@@ -145,4 +148,5 @@ export declare function handleChildSpawnFailed(ctx: ConnectorContext, event: Chi
|
|
|
145
148
|
* covers them.
|
|
146
149
|
*/
|
|
147
150
|
export declare function buildForkScopePrefix(subject: string, threadRootId?: string): string;
|
|
151
|
+
export {};
|
|
148
152
|
//# sourceMappingURL=fork.d.ts.map
|
package/dist/fork.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fork.d.ts","sourceRoot":"","sources":["../src/fork.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"fork.d.ts","sourceRoot":"","sources":["../src/fork.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound-channel.d.ts","sourceRoot":"","sources":["../src/inbound-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"inbound-channel.d.ts","sourceRoot":"","sources":["../src/inbound-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAkB3E;;;;GAIG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,OAAQ,CAAC;AAEhD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAkK5B"}
|
package/dist/inbound-channel.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { channelCapabilityEnv } from './channel-capability.js';
|
|
1
2
|
import { buildChannelPrompt } from './channel-prompt.js';
|
|
2
3
|
import { parallApiUrl, parallOrgId, requireAgk } from './connect.js';
|
|
3
|
-
import {
|
|
4
|
+
import { claimDispatchSource, completeSourceEffect, createChannelInputStep, ensureSession, invalidateSession, nextEnvelopeId, patchSession, readActiveEmit, REQUEST_TIMEOUT_MS, TURN_ENVELOPE_KEY, writeActiveEmit, } from './session.js';
|
|
4
5
|
/**
|
|
5
6
|
* External IM inbound (channel_message dispatches — Feishu/Slack via the
|
|
6
7
|
* platform-mediated channel, external-im-channel-design.md). Split from
|
|
@@ -40,7 +41,7 @@ export async function effectsForChannelDispatch(sourceId, ctx) {
|
|
|
40
41
|
if (m.status === 404 || m.status === 403) {
|
|
41
42
|
// Deleted / inaccessible (also the shape a disabled org feature flag
|
|
42
43
|
// takes) — permanently unusable, drop it.
|
|
43
|
-
return [
|
|
44
|
+
return [completeSourceEffect(ctx, 'channel_message', sourceId)];
|
|
44
45
|
}
|
|
45
46
|
return []; // transient: keep pending, the catch-up sweep retries
|
|
46
47
|
}
|
|
@@ -49,18 +50,18 @@ export async function effectsForChannelDispatch(sourceId, ctx) {
|
|
|
49
50
|
const text = msg?.text ?? '';
|
|
50
51
|
if (!conversationId) {
|
|
51
52
|
// No conversation → no session key and no reply target: unusable.
|
|
52
|
-
return [
|
|
53
|
+
return [completeSourceEffect(ctx, 'channel_message', sourceId)];
|
|
53
54
|
}
|
|
54
55
|
if (!text) {
|
|
55
56
|
// The ingress adapter only persists mechanically parsed text; a row with
|
|
56
57
|
// none will never grow any (same reasoning as attachment-only chat
|
|
57
58
|
// messages) — drop rather than let it pile up in the pending queue.
|
|
58
|
-
return [
|
|
59
|
+
return [completeSourceEffect(ctx, 'channel_message', sourceId)];
|
|
59
60
|
}
|
|
60
61
|
const c = await get(`/channel-conversations/${conversationId}`);
|
|
61
62
|
if (!c.ok) {
|
|
62
63
|
if (c.status === 404 || c.status === 403) {
|
|
63
|
-
return [
|
|
64
|
+
return [completeSourceEffect(ctx, 'channel_message', sourceId)];
|
|
64
65
|
}
|
|
65
66
|
return [];
|
|
66
67
|
}
|
|
@@ -78,7 +79,7 @@ export async function effectsForChannelDispatch(sourceId, ctx) {
|
|
|
78
79
|
// (see the comments there): received settles before this function returns;
|
|
79
80
|
// the turnEnvelope marker goes down before any session write; the row
|
|
80
81
|
// stays 'received' until the turn event's complete-sources.
|
|
81
|
-
const receivedSettled =
|
|
82
|
+
const receivedSettled = claimDispatchSource(ctx, 'channel_message', sourceId);
|
|
82
83
|
const priorEmit = await readActiveEmit(ctx, 'channel_message', sourceId);
|
|
83
84
|
const envelopeId = nextEnvelopeId('channel_message', sourceId, priorEmit);
|
|
84
85
|
let sessionId = await ensureSession(ctx);
|
|
@@ -116,6 +117,9 @@ export async function effectsForChannelDispatch(sourceId, ctx) {
|
|
|
116
117
|
}
|
|
117
118
|
await receivedSettled;
|
|
118
119
|
await writeActiveEmit(ctx, 'channel_message', sourceId, envelopeId);
|
|
120
|
+
// Live capability grant → this turn's lark-cli auth env (mint gate = the
|
|
121
|
+
// no-drift revocation point) + the hint teaching the single reply path.
|
|
122
|
+
const capabilityEnv = await channelCapabilityEnv(ctx);
|
|
119
123
|
return [
|
|
120
124
|
{
|
|
121
125
|
type: 'emitEvent',
|
|
@@ -140,6 +144,7 @@ export async function effectsForChannelDispatch(sourceId, ctx) {
|
|
|
140
144
|
externalMessageId,
|
|
141
145
|
senderName,
|
|
142
146
|
text,
|
|
147
|
+
cliCapable: capabilityEnv !== null,
|
|
143
148
|
}),
|
|
144
149
|
conversationId,
|
|
145
150
|
channelMessageId: sourceId,
|
|
@@ -147,19 +152,19 @@ export async function effectsForChannelDispatch(sourceId, ctx) {
|
|
|
147
152
|
externalConversationId,
|
|
148
153
|
externalMessageId,
|
|
149
154
|
},
|
|
150
|
-
// No chatId on purpose: the reply
|
|
151
|
-
//
|
|
152
|
-
//
|
|
155
|
+
// No chatId on purpose: the reply is the agent's own lark-cli send,
|
|
156
|
+
// NOT deliver() — deliver only runs the redundant session idle for
|
|
157
|
+
// channel routes (see delivery.ts).
|
|
153
158
|
replyRoute: {
|
|
154
159
|
kind: 'provider_http',
|
|
155
160
|
connectionId: ctx.connectionId,
|
|
156
161
|
data: { conversationId, envelopeId },
|
|
157
162
|
},
|
|
158
|
-
//
|
|
159
|
-
// a chat that doesn't exist
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
+
// Invocation context carries ONLY the capability env: no PRLL_CHAT_ID
|
|
164
|
+
// (it must not point CLI defaults at a chat that doesn't exist) and
|
|
165
|
+
// no PRLL_DISPATCH_SOURCE_* (the external reply is a vendor-CLI send,
|
|
166
|
+
// and this row's ledger close is the turn event's complete-sources).
|
|
167
|
+
...(capabilityEnv ? { context: capabilityEnv } : {}),
|
|
163
168
|
},
|
|
164
169
|
},
|
|
165
170
|
// No ack effect — see the message path (inbound.ts): the row stays
|
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;AAkH3B;kDACkD;AAClD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CASjE;AACD,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO9D;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAG5B;AAwCD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACtB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAE5B;AA8BD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAE/E;AAkrBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B"}
|