@portel/photon 1.20.0 → 1.20.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/auto-ui/beam/routes/api-config.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.js +161 -20
- package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +4 -3
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/bridge/renderers.d.ts.map +1 -1
- package/dist/auto-ui/bridge/renderers.js +12 -4
- package/dist/auto-ui/bridge/renderers.js.map +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +115 -28
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/beam-form.bundle.js +19 -182
- package/dist/beam-form.bundle.js.map +4 -4
- package/dist/beam.bundle.js +743 -311
- package/dist/beam.bundle.js.map +4 -4
- package/dist/cli/commands/beam.d.ts.map +1 -1
- package/dist/cli/commands/beam.js +47 -30
- package/dist/cli/commands/beam.js.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +27 -2
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/daemon.d.ts.map +1 -1
- package/dist/cli/commands/daemon.js +12 -6
- package/dist/cli/commands/daemon.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +18 -6
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +14 -2
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli-alias.d.ts.map +1 -1
- package/dist/cli-alias.js +2 -3
- package/dist/cli-alias.js.map +1 -1
- package/dist/context-store.d.ts +4 -4
- package/dist/context-store.d.ts.map +1 -1
- package/dist/context-store.js +18 -15
- package/dist/context-store.js.map +1 -1
- package/dist/context.d.ts +25 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +69 -4
- package/dist/context.js.map +1 -1
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +4 -1
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/manager.d.ts +2 -0
- package/dist/daemon/manager.d.ts.map +1 -1
- package/dist/daemon/manager.js +40 -8
- package/dist/daemon/manager.js.map +1 -1
- package/dist/daemon/server.js +59 -15
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/worker-host.js +7 -0
- package/dist/daemon/worker-host.js.map +1 -1
- package/dist/daemon/worker-manager.d.ts.map +1 -1
- package/dist/daemon/worker-manager.js +58 -10
- package/dist/daemon/worker-manager.js.map +1 -1
- package/dist/daemon/worker-protocol.d.ts +3 -0
- package/dist/daemon/worker-protocol.d.ts.map +1 -1
- package/dist/deploy/cloudflare.d.ts.map +1 -1
- package/dist/deploy/cloudflare.js +2 -4
- package/dist/deploy/cloudflare.js.map +1 -1
- package/dist/loader.d.ts +7 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +56 -2
- package/dist/loader.js.map +1 -1
- package/dist/marketplace-manager.d.ts +1 -1
- package/dist/marketplace-manager.d.ts.map +1 -1
- package/dist/marketplace-manager.js +5 -4
- package/dist/marketplace-manager.js.map +1 -1
- package/dist/photon-cli-runner.d.ts.map +1 -1
- package/dist/photon-cli-runner.js +40 -21
- package/dist/photon-cli-runner.js.map +1 -1
- package/dist/photon-doc-extractor.d.ts.map +1 -1
- package/dist/photon-doc-extractor.js +59 -15
- package/dist/photon-doc-extractor.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +14 -16
- package/dist/server.js.map +1 -1
- package/dist/shared-utils.d.ts +4 -0
- package/dist/shared-utils.d.ts.map +1 -1
- package/dist/shared-utils.js +22 -0
- package/dist/shared-utils.js.map +1 -1
- package/dist/template-manager.d.ts.map +1 -1
- package/dist/template-manager.js +56 -234
- package/dist/template-manager.js.map +1 -1
- package/package.json +2 -2
|
@@ -172,6 +172,90 @@ function parseDurationToMs(duration) {
|
|
|
172
172
|
return 5 * 60 * 1000;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
+
// ── Elicitation lifecycle helpers ──
|
|
176
|
+
/** Duration before an unanswered elicitation moves to pending approvals */
|
|
177
|
+
const ELICITATION_DEFER_MS = 30_000; // 30 seconds
|
|
178
|
+
/** Maximum time an elicitation stays alive in pending queue */
|
|
179
|
+
const ELICITATION_EXPIRY_MS = 30 * 60 * 1000; // 30 minutes
|
|
180
|
+
/** Interval between progress keepalive broadcasts */
|
|
181
|
+
const KEEPALIVE_INTERVAL_MS = 25_000; // 25 seconds (under 30s SDK default)
|
|
182
|
+
/** Clean up all timers associated with a pending elicitation */
|
|
183
|
+
function cleanupElicitation(pending) {
|
|
184
|
+
if (pending.timer)
|
|
185
|
+
clearTimeout(pending.timer);
|
|
186
|
+
if (pending.deferTimer)
|
|
187
|
+
clearTimeout(pending.deferTimer);
|
|
188
|
+
if (pending.keepaliveInterval)
|
|
189
|
+
clearInterval(pending.keepaliveInterval);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Set up two-phase timeout for an elicitation:
|
|
193
|
+
* Phase 1 (30s): Modal shown to user. If no response, move to pending queue.
|
|
194
|
+
* Phase 2 (30min): Keepalive progress notifications sent. Final expiry cancels.
|
|
195
|
+
*/
|
|
196
|
+
function setupElicitationTimeout(elicitationId, pending, resolve) {
|
|
197
|
+
const photon = pending.photonName || 'unknown';
|
|
198
|
+
const method = pending.methodName || 'unknown';
|
|
199
|
+
const message = pending.message || 'Approval required';
|
|
200
|
+
// Phase 1: After 30s without response, defer to pending queue
|
|
201
|
+
pending.deferTimer = setTimeout(() => {
|
|
202
|
+
// Only defer if still pending (user may have responded)
|
|
203
|
+
if (!pendingElicitations.has(elicitationId))
|
|
204
|
+
return;
|
|
205
|
+
const approvalId = elicitationId; // reuse ID for linking
|
|
206
|
+
pending.approvalId = approvalId;
|
|
207
|
+
const expiresAt = new Date(Date.now() + ELICITATION_EXPIRY_MS).toISOString();
|
|
208
|
+
// Write to persistent approval storage (fire-and-forget, non-blocking)
|
|
209
|
+
void addApproval({
|
|
210
|
+
id: approvalId,
|
|
211
|
+
photon,
|
|
212
|
+
method,
|
|
213
|
+
message,
|
|
214
|
+
status: 'pending',
|
|
215
|
+
createdAt: new Date().toISOString(),
|
|
216
|
+
expiresAt,
|
|
217
|
+
});
|
|
218
|
+
// Tell Beam frontend to close modal and show badge
|
|
219
|
+
broadcastToBeam('beam/elicitation-deferred', {
|
|
220
|
+
elicitationId,
|
|
221
|
+
approvalId,
|
|
222
|
+
photon,
|
|
223
|
+
method,
|
|
224
|
+
message,
|
|
225
|
+
expiresAt,
|
|
226
|
+
});
|
|
227
|
+
// Start progress keepalives to prevent external MCP client timeout
|
|
228
|
+
pending.keepaliveInterval = setInterval(() => {
|
|
229
|
+
if (!pendingElicitations.has(elicitationId)) {
|
|
230
|
+
if (pending.keepaliveInterval)
|
|
231
|
+
clearInterval(pending.keepaliveInterval);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
broadcastNotification('notifications/progress', {
|
|
235
|
+
progressToken: `approval_${elicitationId}`,
|
|
236
|
+
progress: 0,
|
|
237
|
+
total: 0,
|
|
238
|
+
message: `Waiting for user approval: ${message}`,
|
|
239
|
+
});
|
|
240
|
+
}, KEEPALIVE_INTERVAL_MS);
|
|
241
|
+
// Phase 2: Final expiry after 30 minutes
|
|
242
|
+
pending.timer = setTimeout(() => {
|
|
243
|
+
if (pendingElicitations.has(elicitationId)) {
|
|
244
|
+
cleanupElicitation(pending);
|
|
245
|
+
pendingElicitations.delete(elicitationId);
|
|
246
|
+
// Mark approval as expired on disk
|
|
247
|
+
void resolveApproval(photon, approvalId, 'rejected');
|
|
248
|
+
// Notify frontend
|
|
249
|
+
broadcastToBeam('beam/approval-resolved', {
|
|
250
|
+
approvalId,
|
|
251
|
+
photon,
|
|
252
|
+
status: 'expired',
|
|
253
|
+
});
|
|
254
|
+
resolve({ action: 'cancel' });
|
|
255
|
+
}
|
|
256
|
+
}, ELICITATION_EXPIRY_MS);
|
|
257
|
+
}, ELICITATION_DEFER_MS);
|
|
258
|
+
}
|
|
175
259
|
// Clean up old sessions periodically (30 min timeout)
|
|
176
260
|
const SESSION_TIMEOUT_MS = 30 * 60 * 1000;
|
|
177
261
|
let sessionCleanupInterval = null;
|
|
@@ -546,8 +630,17 @@ const handlers = {
|
|
|
546
630
|
};
|
|
547
631
|
}
|
|
548
632
|
pendingElicitations.delete(elicitationId);
|
|
549
|
-
|
|
550
|
-
|
|
633
|
+
cleanupElicitation(pending);
|
|
634
|
+
// If this elicitation was deferred to approvals, resolve the approval on disk too
|
|
635
|
+
if (pending.approvalId) {
|
|
636
|
+
const status = params?.cancelled ? 'rejected' : 'approved';
|
|
637
|
+
await resolveApproval(pending.photonName || 'unknown', pending.approvalId, status);
|
|
638
|
+
broadcastToBeam('beam/approval-resolved', {
|
|
639
|
+
approvalId: pending.approvalId,
|
|
640
|
+
photon: pending.photonName || 'unknown',
|
|
641
|
+
status,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
551
644
|
if (params?.cancelled) {
|
|
552
645
|
pending.reject(new Error('Elicitation cancelled by user'));
|
|
553
646
|
}
|
|
@@ -580,8 +673,7 @@ const handlers = {
|
|
|
580
673
|
const pending = pendingElicitations.get(params.approvalId);
|
|
581
674
|
if (pending) {
|
|
582
675
|
pendingElicitations.delete(params.approvalId);
|
|
583
|
-
|
|
584
|
-
clearTimeout(pending.timer);
|
|
676
|
+
cleanupElicitation(pending);
|
|
585
677
|
if (params.approved) {
|
|
586
678
|
pending.resolve(true);
|
|
587
679
|
}
|
|
@@ -1159,7 +1251,7 @@ const handlers = {
|
|
|
1159
1251
|
const elicitResult = await requestBeamElicitation({
|
|
1160
1252
|
ask: 'confirm',
|
|
1161
1253
|
message: `"${methodName}" is a destructive operation. Continue?`,
|
|
1162
|
-
});
|
|
1254
|
+
}, { photonName: serverName, methodName });
|
|
1163
1255
|
if (elicitResult.action !== 'accept' || elicitResult.content === false) {
|
|
1164
1256
|
return {
|
|
1165
1257
|
jsonrpc: '2.0',
|
|
@@ -1203,7 +1295,7 @@ const handlers = {
|
|
|
1203
1295
|
ask: 'select',
|
|
1204
1296
|
message: 'Select an instance',
|
|
1205
1297
|
options: selectOptions,
|
|
1206
|
-
});
|
|
1298
|
+
}, { photonName: serverName, methodName });
|
|
1207
1299
|
if (elicitResult.action !== 'accept' || !elicitResult.content) {
|
|
1208
1300
|
return {
|
|
1209
1301
|
jsonrpc: '2.0',
|
|
@@ -1221,7 +1313,7 @@ const handlers = {
|
|
|
1221
1313
|
ask: 'text',
|
|
1222
1314
|
message: 'Enter a name for the new instance',
|
|
1223
1315
|
placeholder: 'e.g. groceries, work, personal',
|
|
1224
|
-
});
|
|
1316
|
+
}, { photonName: serverName, methodName });
|
|
1225
1317
|
if (nameResult.action !== 'accept' || !nameResult.content) {
|
|
1226
1318
|
return {
|
|
1227
1319
|
jsonrpc: '2.0',
|
|
@@ -2100,7 +2192,7 @@ const handlers = {
|
|
|
2100
2192
|
}
|
|
2101
2193
|
// If input_required right now, handle elicitation before waiting
|
|
2102
2194
|
if (task.state === 'input_required' && task.input) {
|
|
2103
|
-
const elicitResult = await requestBeamElicitation(task.input);
|
|
2195
|
+
const elicitResult = await requestBeamElicitation(task.input, { photonName: task.photon || 'task', methodName: task.method || taskId });
|
|
2104
2196
|
if (elicitResult.action === 'accept') {
|
|
2105
2197
|
resolveTaskInput(taskId, elicitResult.content);
|
|
2106
2198
|
}
|
|
@@ -2121,7 +2213,7 @@ const handlers = {
|
|
|
2121
2213
|
}
|
|
2122
2214
|
if (current.state === 'input_required' && current.input) {
|
|
2123
2215
|
// Send elicitation to the client
|
|
2124
|
-
const elicitResult = await requestBeamElicitation(current.input);
|
|
2216
|
+
const elicitResult = await requestBeamElicitation(current.input, { photonName: task.photon || 'task', methodName: task.method || taskId });
|
|
2125
2217
|
if (elicitResult.action === 'accept') {
|
|
2126
2218
|
resolveTaskInput(taskId, elicitResult.content);
|
|
2127
2219
|
}
|
|
@@ -2408,7 +2500,7 @@ async function handleBeamRemove(req, ctx, args) {
|
|
|
2408
2500
|
const elicitResult = await requestBeamElicitation({
|
|
2409
2501
|
ask: 'confirm',
|
|
2410
2502
|
message: `Remove "${photonName}"? The photon and its assets will be moved to trash.`,
|
|
2411
|
-
});
|
|
2503
|
+
}, { photonName, methodName: 'remove' });
|
|
2412
2504
|
if (elicitResult.action !== 'accept' || elicitResult.content === false) {
|
|
2413
2505
|
return {
|
|
2414
2506
|
jsonrpc: '2.0',
|
|
@@ -3416,8 +3508,7 @@ export function sendToSession(sessionId, method, params) {
|
|
|
3416
3508
|
*/
|
|
3417
3509
|
export function requestExternalElicitation(mcpName, request) {
|
|
3418
3510
|
const elicitationId = randomUUID();
|
|
3419
|
-
return new Promise((resolve
|
|
3420
|
-
// Store pending elicitation
|
|
3511
|
+
return new Promise((resolve) => {
|
|
3421
3512
|
const pending = {
|
|
3422
3513
|
resolve: (value) => {
|
|
3423
3514
|
resolve({ action: 'accept', content: value });
|
|
@@ -3430,7 +3521,10 @@ export function requestExternalElicitation(mcpName, request) {
|
|
|
3430
3521
|
resolve({ action: 'decline' });
|
|
3431
3522
|
}
|
|
3432
3523
|
},
|
|
3433
|
-
sessionId: '',
|
|
3524
|
+
sessionId: '',
|
|
3525
|
+
photonName: mcpName,
|
|
3526
|
+
methodName: 'elicitation',
|
|
3527
|
+
message: request.message,
|
|
3434
3528
|
};
|
|
3435
3529
|
pendingElicitations.set(elicitationId, pending);
|
|
3436
3530
|
// Broadcast elicitation request to all Beam clients
|
|
@@ -3442,13 +3536,8 @@ export function requestExternalElicitation(mcpName, request) {
|
|
|
3442
3536
|
schema: request.requestedSchema,
|
|
3443
3537
|
url: request.url,
|
|
3444
3538
|
});
|
|
3445
|
-
//
|
|
3446
|
-
pending
|
|
3447
|
-
if (pendingElicitations.has(elicitationId)) {
|
|
3448
|
-
pendingElicitations.delete(elicitationId);
|
|
3449
|
-
resolve({ action: 'cancel' });
|
|
3450
|
-
}
|
|
3451
|
-
}, 300000);
|
|
3539
|
+
// Two-phase timeout: 30s modal → pending queue with keepalives → 30min expiry
|
|
3540
|
+
setupElicitationTimeout(elicitationId, pending, resolve);
|
|
3452
3541
|
});
|
|
3453
3542
|
}
|
|
3454
3543
|
/**
|
|
@@ -3456,7 +3545,7 @@ export function requestExternalElicitation(mcpName, request) {
|
|
|
3456
3545
|
* Unlike requestExternalElicitation which uses MCP form/url mode, this sends
|
|
3457
3546
|
* the ask type directly so the elicitation modal renders the appropriate UI.
|
|
3458
3547
|
*/
|
|
3459
|
-
function requestBeamElicitation(data) {
|
|
3548
|
+
function requestBeamElicitation(data, context) {
|
|
3460
3549
|
const elicitationId = randomUUID();
|
|
3461
3550
|
return new Promise((resolve) => {
|
|
3462
3551
|
const pending = {
|
|
@@ -3472,6 +3561,9 @@ function requestBeamElicitation(data) {
|
|
|
3472
3561
|
}
|
|
3473
3562
|
},
|
|
3474
3563
|
sessionId: '',
|
|
3564
|
+
photonName: context?.photonName,
|
|
3565
|
+
methodName: context?.methodName,
|
|
3566
|
+
message: data.message,
|
|
3475
3567
|
};
|
|
3476
3568
|
pendingElicitations.set(elicitationId, pending);
|
|
3477
3569
|
// Broadcast with Photon-native ask format (not MCP form mode)
|
|
@@ -3479,13 +3571,8 @@ function requestBeamElicitation(data) {
|
|
|
3479
3571
|
elicitationId,
|
|
3480
3572
|
...data,
|
|
3481
3573
|
});
|
|
3482
|
-
//
|
|
3483
|
-
pending
|
|
3484
|
-
if (pendingElicitations.has(elicitationId)) {
|
|
3485
|
-
pendingElicitations.delete(elicitationId);
|
|
3486
|
-
resolve({ action: 'cancel' });
|
|
3487
|
-
}
|
|
3488
|
-
}, 300000);
|
|
3574
|
+
// Two-phase timeout: 30s modal → pending queue with keepalives → 30min expiry
|
|
3575
|
+
setupElicitationTimeout(elicitationId, pending, resolve);
|
|
3489
3576
|
});
|
|
3490
3577
|
}
|
|
3491
3578
|
//# sourceMappingURL=streamable-http-transport.js.map
|