@undefineds.co/linx 0.3.5 → 0.3.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/README.md +58 -23
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/index.js +336 -162
- package/dist/index.js.map +1 -1
- package/dist/lib/account-session.js +4 -8
- package/dist/lib/account-session.js.map +1 -1
- package/dist/lib/ai-command.js +228 -178
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/auto-mode/archive.js +38 -7
- package/dist/lib/auto-mode/archive.js.map +1 -1
- package/dist/lib/auto-mode/auth.js.map +1 -1
- package/dist/lib/auto-mode/display.js +71 -45
- package/dist/lib/auto-mode/display.js.map +1 -1
- package/dist/lib/auto-mode/format.js +9 -7
- package/dist/lib/auto-mode/format.js.map +1 -1
- package/dist/lib/auto-mode/hooks/claude.js +12 -2
- package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
- package/dist/lib/auto-mode/hooks/codex.js +17 -7
- package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
- package/dist/lib/auto-mode/hooks/index.js +28 -8
- package/dist/lib/auto-mode/hooks/index.js.map +1 -1
- package/dist/lib/auto-mode/pod-ai.js +20 -37
- package/dist/lib/auto-mode/pod-ai.js.map +1 -1
- package/dist/lib/auto-mode/pod-approval.js +124 -195
- package/dist/lib/auto-mode/pod-approval.js.map +1 -1
- package/dist/lib/auto-mode/pod-persistence.js +169 -90
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
- package/dist/lib/auto-mode/runner.js +683 -81
- package/dist/lib/auto-mode/runner.js.map +1 -1
- package/dist/lib/auto-mode/secretary.js +186 -41
- package/dist/lib/auto-mode/secretary.js.map +1 -1
- package/dist/lib/auto-mode-command.js +32 -32
- package/dist/lib/auto-mode-command.js.map +1 -1
- package/dist/lib/chat-api.js +242 -50
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/bridge.js +164 -17
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/credentials-store.js +33 -42
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/linx-cloud-errors.js +61 -0
- package/dist/lib/linx-cloud-errors.js.map +1 -0
- package/dist/lib/linx-tui-contract.js +8 -5
- package/dist/lib/linx-tui-contract.js.map +1 -1
- package/dist/lib/login-command.js +9 -2
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +3 -20
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +143 -17
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js +2 -6
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
- package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
- package/dist/lib/pi-adapter/backend-command.js +2 -0
- package/dist/lib/pi-adapter/backend-command.js.map +1 -0
- package/dist/lib/pi-adapter/backend-credentials.js +80 -0
- package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
- package/dist/lib/pi-adapter/branding.js +246 -108
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/control-state.js +72 -0
- package/dist/lib/pi-adapter/control-state.js.map +1 -0
- package/dist/lib/pi-adapter/interactive.js +2634 -30
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +382 -210
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +531 -64
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +81 -85
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/pod-status-output.js +54 -0
- package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
- package/dist/lib/pi-adapter/runtime.js +458 -228
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session-control.js +509 -0
- package/dist/lib/pi-adapter/session-control.js.map +1 -0
- package/dist/lib/pi-adapter/session.js +35 -22
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +89 -32
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/sync-recovery.js +89 -0
- package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
- package/dist/lib/pi-adapter/web-fetch.js +13 -14
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
- package/dist/lib/pod-chat-store.js +254 -78
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +156 -35
- package/dist/lib/pod-data-session.js.map +1 -1
- package/dist/lib/solid-auth-store.js +27 -0
- package/dist/lib/solid-auth-store.js.map +1 -0
- package/dist/lib/solid-auth.js +2 -4
- package/dist/lib/solid-auth.js.map +1 -1
- package/dist/lib/solid-client-credentials-login.js +100 -0
- package/dist/lib/solid-client-credentials-login.js.map +1 -0
- package/dist/lib/solid-local-store.js +31 -0
- package/dist/lib/solid-local-store.js.map +1 -0
- package/dist/lib/symphony/archive.js +328 -18
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +2222 -0
- package/dist/lib/symphony/pod-projection.js.map +1 -0
- package/dist/lib/symphony-command.js +602 -178
- package/dist/lib/symphony-command.js.map +1 -1
- package/dist/lib/sync-checkpoint-store.js +74 -0
- package/dist/lib/sync-checkpoint-store.js.map +1 -0
- package/dist/skills/symphony/SKILL.md +665 -0
- package/package.json +15 -9
- package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
- package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
- package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
- package/vendor/agent-runtime/dist/auto-mode.js +288 -31
- package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
- package/vendor/agent-runtime/dist/control-plane.js +79 -0
- package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
- package/vendor/agent-runtime/dist/file-sync.js +314 -0
- package/vendor/agent-runtime/dist/index.d.ts +7 -0
- package/vendor/agent-runtime/dist/index.js +7 -0
- package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
- package/vendor/agent-runtime/dist/reconciler.js +361 -0
- package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
- package/vendor/agent-runtime/dist/symphony.js +362 -57
- package/vendor/agent-runtime/dist/sync.d.ts +271 -0
- package/vendor/agent-runtime/dist/sync.js +550 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
- package/vendor/agent-runtime/dist/turn-controller.js +2 -2
- package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
- package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
- package/vendor/agent-runtime/package.json +8 -1
- package/vendor/pi-web-access/CHANGELOG.md +387 -0
- package/vendor/pi-web-access/LICENSE +21 -0
- package/vendor/pi-web-access/README.md +352 -0
- package/vendor/pi-web-access/activity.ts +101 -0
- package/vendor/pi-web-access/banner.png +0 -0
- package/vendor/pi-web-access/chrome-cookies.ts +322 -0
- package/vendor/pi-web-access/code-search.ts +107 -0
- package/vendor/pi-web-access/curator-page.ts +3359 -0
- package/vendor/pi-web-access/curator-server.ts +605 -0
- package/vendor/pi-web-access/exa.ts +520 -0
- package/vendor/pi-web-access/extract.ts +641 -0
- package/vendor/pi-web-access/gemini-api.ts +112 -0
- package/vendor/pi-web-access/gemini-search.ts +361 -0
- package/vendor/pi-web-access/gemini-url-context.ts +126 -0
- package/vendor/pi-web-access/gemini-web-config.ts +52 -0
- package/vendor/pi-web-access/gemini-web.ts +396 -0
- package/vendor/pi-web-access/github-api.ts +196 -0
- package/vendor/pi-web-access/github-extract.ts +634 -0
- package/vendor/pi-web-access/index.ts +2346 -0
- package/vendor/pi-web-access/package.json +45 -0
- package/vendor/pi-web-access/pdf-extract.ts +192 -0
- package/vendor/pi-web-access/perplexity.ts +195 -0
- package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
- package/vendor/pi-web-access/rsc-extract.ts +338 -0
- package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
- package/vendor/pi-web-access/storage.ts +72 -0
- package/vendor/pi-web-access/summary-review.ts +276 -0
- package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
- package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
- package/vendor/pi-web-access/utils.ts +44 -0
- package/vendor/pi-web-access/video-extract.ts +378 -0
- package/vendor/pi-web-access/youtube-extract.ts +310 -0
- package/dist/lib/pi-adapter/auth.js +0 -68
- package/dist/lib/pi-adapter/auth.js.map +0 -1
- package/dist/lib/pi-adapter/pod-tools.js +0 -140
- package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
- package/dist/skills/drizzle-solid/SKILL.md +0 -340
- package/dist/skills/pod-storage/SKILL.md +0 -100
- package/dist/skills/solid-modeling/SKILL.md +0 -274
- package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
|
@@ -1,246 +1,418 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
request,
|
|
20
|
-
record: buildPiApprovalRecord(options, context),
|
|
21
|
-
mode: options.mode ?? 'smart',
|
|
22
|
-
pollMs: options.pollMs,
|
|
23
|
-
signal,
|
|
24
|
-
runtime: options.runtime,
|
|
25
|
-
resolveSecretaryRecommendation: options.resolveSecretaryRecommendation ?? resolveAutoModeSecretaryRecommendation,
|
|
26
|
-
});
|
|
27
|
-
return mapApprovalDecisionToBeforeToolCallResult(decision, request);
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createRemoteApproval, resolveRemoteAutoModeApproval, waitForRemoteAutoModeApproval, } from '../auto-mode/pod-approval.js';
|
|
3
|
+
import { DEFAULT_SECRETARY_CHAT_ID, secretaryAgentUri, secretaryThreadUri } from './pod-mirror-mapping.js';
|
|
4
|
+
const EXTENSION_UI_POLICY_VERSION = 'linx-pi-extension-ui/v1';
|
|
5
|
+
const DEFAULT_POLL_MS = 1000;
|
|
6
|
+
const EXTENSION_UI_INPUT_QUESTION_ID = 'runtime';
|
|
7
|
+
export function createPodBackedExtensionUiContext(baseUi, options = {}) {
|
|
8
|
+
return {
|
|
9
|
+
...baseUi,
|
|
10
|
+
select(title, choices, opts) {
|
|
11
|
+
return selectWithPodApproval(baseUi, title, choices, opts, options);
|
|
12
|
+
},
|
|
13
|
+
confirm(title, message, opts) {
|
|
14
|
+
return confirmWithPodApproval(baseUi, title, message, opts, options);
|
|
15
|
+
},
|
|
16
|
+
input(title, placeholder, opts) {
|
|
17
|
+
return inputWithAutoSecretary(baseUi, title, placeholder, opts, options);
|
|
18
|
+
},
|
|
28
19
|
};
|
|
29
20
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
mode: input.mode,
|
|
37
|
-
record: input.record,
|
|
38
|
-
request: input.request,
|
|
39
|
-
}).catch(() => createFallbackAutoModeSecretaryRecommendation({
|
|
40
|
-
mode: input.mode,
|
|
41
|
-
request: input.request,
|
|
42
|
-
}));
|
|
43
|
-
const recommendation = normalizePiApprovalRecommendation(input, rawRecommendation);
|
|
44
|
-
if (recommendation?.canAutoDecide && recommendation.decision && recommendation.source === 'model') {
|
|
45
|
-
return resolvePiRemoteApproval(input, recommendation);
|
|
21
|
+
async function selectWithPodApproval(baseUi, title, choices, opts, options) {
|
|
22
|
+
if (opts?.signal?.aborted || choices.length === 0) {
|
|
23
|
+
return baseUi.select(title, choices, opts);
|
|
24
|
+
}
|
|
25
|
+
if (!shouldPodBackSelect(choices)) {
|
|
26
|
+
return baseUi.select(title, choices, opts);
|
|
46
27
|
}
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
28
|
+
const approvalOptions = choices.map((label, index) => buildSelectApprovalOption(label, index));
|
|
29
|
+
const autoResult = await resolveAutoSecretaryExtensionUiInput({
|
|
30
|
+
title,
|
|
31
|
+
kind: 'select',
|
|
32
|
+
choices,
|
|
33
|
+
approvalOptions,
|
|
34
|
+
timeoutMs: opts?.timeout,
|
|
35
|
+
options,
|
|
36
|
+
mapResponse: (response) => {
|
|
37
|
+
if (!response) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (response.kind === 'approval') {
|
|
41
|
+
return choiceFromRemoteDecision(response.decision, choices, approvalOptions);
|
|
42
|
+
}
|
|
43
|
+
const answer = response.answers[EXTENSION_UI_INPUT_QUESTION_ID]?.answers[0];
|
|
44
|
+
if (!answer) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return choices.includes(answer) ? answer : undefined;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
if (autoResult.resolved) {
|
|
51
|
+
return autoResult.value;
|
|
50
52
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
const result = await raceLocalAndRemote({
|
|
54
|
+
title,
|
|
55
|
+
kind: 'select',
|
|
56
|
+
choices,
|
|
57
|
+
approvalOptions,
|
|
58
|
+
opts,
|
|
59
|
+
options,
|
|
60
|
+
runLocal: (signal) => baseUi.select(title, choices, { ...opts, signal }),
|
|
61
|
+
mapLocalToDecision: (selected) => podDecisionFromSelectedChoice(selected, approvalOptions),
|
|
62
|
+
mapRemoteToLocal: (decision) => choiceFromRemoteDecision(decision, choices, approvalOptions),
|
|
63
|
+
localResolutionNote: (selected) => selected
|
|
64
|
+
? encodeExtensionUiNote({ kind: 'select', selectedOptionId: optionIdForChoice(selected, choices), selectedLabel: selected })
|
|
65
|
+
: encodeExtensionUiNote({ kind: 'select', cancelled: true }),
|
|
66
|
+
});
|
|
67
|
+
return result;
|
|
53
68
|
}
|
|
54
|
-
function
|
|
55
|
-
if (
|
|
56
|
-
return
|
|
69
|
+
async function confirmWithPodApproval(baseUi, title, message, opts, options) {
|
|
70
|
+
if (opts?.signal?.aborted) {
|
|
71
|
+
return baseUi.confirm(title, message, opts);
|
|
72
|
+
}
|
|
73
|
+
const approvalOptions = [
|
|
74
|
+
{ optionId: 'yes', label: 'Yes', kind: 'allow_once' },
|
|
75
|
+
{ optionId: 'no', label: 'No', kind: 'reject_once' },
|
|
76
|
+
];
|
|
77
|
+
const autoResult = await resolveAutoSecretaryExtensionUiInput({
|
|
78
|
+
title,
|
|
79
|
+
message,
|
|
80
|
+
kind: 'confirm',
|
|
81
|
+
choices: ['Yes', 'No'],
|
|
82
|
+
approvalOptions,
|
|
83
|
+
timeoutMs: opts?.timeout,
|
|
84
|
+
options,
|
|
85
|
+
mapResponse: (response) => {
|
|
86
|
+
if (!response) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
if (response.kind === 'approval') {
|
|
90
|
+
return response.decision === 'accept' || response.decision === 'accept_for_session';
|
|
91
|
+
}
|
|
92
|
+
const answer = response.answers[EXTENSION_UI_INPUT_QUESTION_ID]?.answers[0]?.toLowerCase();
|
|
93
|
+
if (!answer) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
if (['yes', 'y', 'true', 'allow', 'approve', 'confirm'].includes(answer)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (['no', 'n', 'false', 'deny', 'decline', 'reject'].includes(answer)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
if (autoResult.resolved) {
|
|
106
|
+
return autoResult.value ?? false;
|
|
107
|
+
}
|
|
108
|
+
const result = await raceLocalAndRemote({
|
|
109
|
+
title,
|
|
110
|
+
message,
|
|
111
|
+
kind: 'confirm',
|
|
112
|
+
choices: ['Yes', 'No'],
|
|
113
|
+
approvalOptions,
|
|
114
|
+
opts,
|
|
115
|
+
options,
|
|
116
|
+
runLocal: (signal) => baseUi.confirm(title, message, { ...opts, signal }),
|
|
117
|
+
mapLocalToDecision: (confirmed) => confirmed ? 'accept' : 'decline',
|
|
118
|
+
mapRemoteToLocal: (decision) => decision === 'accept' || decision === 'accept_for_session',
|
|
119
|
+
localResolutionNote: (confirmed) => encodeExtensionUiNote({
|
|
120
|
+
kind: 'confirm',
|
|
121
|
+
selectedOptionId: confirmed ? 'yes' : 'no',
|
|
122
|
+
selectedLabel: confirmed ? 'Yes' : 'No',
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
return result ?? false;
|
|
126
|
+
}
|
|
127
|
+
async function inputWithAutoSecretary(baseUi, title, placeholder, opts, options) {
|
|
128
|
+
if (opts?.signal?.aborted) {
|
|
129
|
+
return baseUi.input(title, placeholder, opts);
|
|
130
|
+
}
|
|
131
|
+
const autoResult = await resolveAutoSecretaryExtensionUiInput({
|
|
132
|
+
title,
|
|
133
|
+
message: placeholder,
|
|
134
|
+
kind: 'input',
|
|
135
|
+
choices: [],
|
|
136
|
+
approvalOptions: [],
|
|
137
|
+
timeoutMs: opts?.timeout,
|
|
138
|
+
options,
|
|
139
|
+
mapResponse: (response) => {
|
|
140
|
+
if (!response) {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
if (response.kind !== 'user-input') {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
return response.answers[EXTENSION_UI_INPUT_QUESTION_ID]?.answers[0];
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
if (autoResult.resolved) {
|
|
150
|
+
return autoResult.value;
|
|
151
|
+
}
|
|
152
|
+
return baseUi.input(title, placeholder, opts);
|
|
153
|
+
}
|
|
154
|
+
async function resolveAutoSecretaryExtensionUiInput(input) {
|
|
155
|
+
const sessionControl = input.options.sessionControl;
|
|
156
|
+
if (!sessionControl) {
|
|
157
|
+
return { resolved: false };
|
|
57
158
|
}
|
|
58
|
-
|
|
159
|
+
const request = buildExtensionUiInteractionRequest(input);
|
|
160
|
+
const response = await sessionControl.resolveInteractionRequest({ request });
|
|
161
|
+
if (!response) {
|
|
162
|
+
return { resolved: false };
|
|
163
|
+
}
|
|
164
|
+
const value = input.mapResponse(response);
|
|
165
|
+
return value === undefined ? { resolved: false } : { resolved: true, value };
|
|
166
|
+
}
|
|
167
|
+
function buildExtensionUiInteractionRequest(input) {
|
|
168
|
+
if (input.kind === 'input' || input.approvalOptions.length === 0) {
|
|
59
169
|
return {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
170
|
+
kind: 'user-input',
|
|
171
|
+
message: [input.title, input.message].filter(Boolean).join('\n') || 'Input required',
|
|
172
|
+
questions: [buildExtensionUiQuestion(input)],
|
|
173
|
+
...(input.timeoutMs ? { timeoutMs: input.timeoutMs } : {}),
|
|
174
|
+
raw: buildExtensionUiRaw(input),
|
|
63
175
|
};
|
|
64
176
|
}
|
|
65
|
-
return
|
|
177
|
+
return {
|
|
178
|
+
kind: 'codex-approval',
|
|
179
|
+
message: [input.title, input.message].filter(Boolean).join('\n') || 'Approval required',
|
|
180
|
+
approvalOptions: input.approvalOptions,
|
|
181
|
+
...(input.timeoutMs ? { timeoutMs: input.timeoutMs } : {}),
|
|
182
|
+
raw: buildExtensionUiRaw(input),
|
|
183
|
+
};
|
|
66
184
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
185
|
+
function buildExtensionUiQuestion(input) {
|
|
186
|
+
return {
|
|
187
|
+
id: EXTENSION_UI_INPUT_QUESTION_ID,
|
|
188
|
+
header: input.kind === 'input' ? 'Input' : 'Select',
|
|
189
|
+
question: [input.title, input.message].filter(Boolean).join('\n') || 'Input required',
|
|
190
|
+
options: input.choices.map((label) => ({ label })),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function buildExtensionUiRaw(input) {
|
|
194
|
+
return {
|
|
195
|
+
source: 'pi-extension-ui',
|
|
196
|
+
kind: input.kind,
|
|
197
|
+
title: input.title,
|
|
198
|
+
...(input.message ? { message: input.message } : {}),
|
|
199
|
+
choices: input.choices,
|
|
200
|
+
...(input.options.cwd ? { cwd: input.options.cwd } : {}),
|
|
201
|
+
sessionId: resolveSessionId(input.options.sessionId),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
async function raceLocalAndRemote(input) {
|
|
205
|
+
const localAbort = new AbortController();
|
|
206
|
+
const removeInputAbort = linkAbortSignal(input.opts?.signal, () => localAbort.abort());
|
|
207
|
+
const remoteAbort = new AbortController();
|
|
208
|
+
const removeRemoteAbort = linkAbortSignal(input.opts?.signal, () => remoteAbort.abort());
|
|
209
|
+
const localPromise = input.runLocal(localAbort.signal).then((value) => ({
|
|
210
|
+
source: 'local',
|
|
211
|
+
value,
|
|
212
|
+
}));
|
|
213
|
+
const remoteReadyPromise = createExtensionUiRemoteApproval({
|
|
214
|
+
title: input.title,
|
|
215
|
+
message: input.message,
|
|
216
|
+
kind: input.kind,
|
|
217
|
+
choices: input.choices,
|
|
218
|
+
approvalOptions: input.approvalOptions,
|
|
219
|
+
timeoutMs: input.opts?.timeout,
|
|
220
|
+
options: input.options,
|
|
221
|
+
}).catch((error) => {
|
|
222
|
+
input.options.onWarning?.(error);
|
|
223
|
+
return null;
|
|
224
|
+
});
|
|
225
|
+
const remotePromise = remoteReadyPromise.then(async (remote) => {
|
|
226
|
+
if (!remote) {
|
|
227
|
+
return { source: 'remote-unavailable' };
|
|
228
|
+
}
|
|
229
|
+
const decision = await waitForRemoteAutoModeApproval({
|
|
77
230
|
approvalId: remote.id,
|
|
78
231
|
approvalUri: remote.approvalUri,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
subject,
|
|
88
|
-
request,
|
|
89
|
-
pollMs: input.pollMs,
|
|
90
|
-
signal: input.signal,
|
|
91
|
-
runtime: input.runtime,
|
|
232
|
+
pollMs: input.options.pollMs ?? DEFAULT_POLL_MS,
|
|
233
|
+
signal: remoteAbort.signal,
|
|
234
|
+
runtime: input.options.runtime,
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
source: 'remote',
|
|
238
|
+
value: input.mapRemoteToLocal(decision),
|
|
239
|
+
};
|
|
92
240
|
});
|
|
241
|
+
void localPromise.catch(() => undefined);
|
|
242
|
+
void remotePromise.catch(() => undefined);
|
|
243
|
+
try {
|
|
244
|
+
const winner = await Promise.race([localPromise, remotePromise]);
|
|
245
|
+
if (winner.source === 'remote-unavailable') {
|
|
246
|
+
return (await localPromise).value;
|
|
247
|
+
}
|
|
248
|
+
if (winner.source === 'local') {
|
|
249
|
+
remoteAbort.abort();
|
|
250
|
+
void remoteReadyPromise.then((remote) => {
|
|
251
|
+
if (!remote) {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
return resolveRemoteAutoModeApproval({
|
|
255
|
+
approvalId: remote.id,
|
|
256
|
+
approvalUri: remote.approvalUri,
|
|
257
|
+
decision: input.mapLocalToDecision(winner.value),
|
|
258
|
+
decisionRole: 'human',
|
|
259
|
+
note: input.localResolutionNote(winner.value),
|
|
260
|
+
runtime: input.options.runtime,
|
|
261
|
+
});
|
|
262
|
+
}).catch((error) => input.options.onWarning?.(error));
|
|
263
|
+
return winner.value;
|
|
264
|
+
}
|
|
265
|
+
localAbort.abort();
|
|
266
|
+
return winner.value;
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
removeInputAbort();
|
|
270
|
+
removeRemoteAbort();
|
|
271
|
+
}
|
|
93
272
|
}
|
|
94
|
-
function
|
|
95
|
-
return ({
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
273
|
+
async function createExtensionUiRemoteApproval(input) {
|
|
274
|
+
return createRemoteApproval({
|
|
275
|
+
subject: ({ webId }) => buildExtensionUiApprovalSubject(webId, input.options),
|
|
276
|
+
request: ({ sessionUri }) => buildExtensionUiApprovalRequest({
|
|
277
|
+
title: input.title,
|
|
278
|
+
message: input.message,
|
|
279
|
+
kind: input.kind,
|
|
280
|
+
choices: input.choices,
|
|
281
|
+
approvalOptions: input.approvalOptions,
|
|
282
|
+
sessionUri,
|
|
283
|
+
cwd: input.options.cwd,
|
|
284
|
+
timeoutMs: input.timeoutMs,
|
|
285
|
+
}),
|
|
286
|
+
runtime: input.options.runtime,
|
|
99
287
|
});
|
|
100
288
|
}
|
|
101
|
-
function
|
|
102
|
-
const
|
|
289
|
+
function buildExtensionUiApprovalSubject(webId, options) {
|
|
290
|
+
const sessionId = resolveSessionId(options.sessionId) ?? 'linx-pi-extension-ui';
|
|
291
|
+
const sessionUri = secretaryThreadUri(webId, sessionId, DEFAULT_SECRETARY_CHAT_ID);
|
|
103
292
|
return {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
action: details.action,
|
|
109
|
-
risk: details.risk,
|
|
110
|
-
...(request.kind === 'command-approval' && request.command ? { command: request.command } : {}),
|
|
111
|
-
...(request.kind === 'command-approval' && request.cwd ? { cwd: request.cwd } : {}),
|
|
293
|
+
sessionUri,
|
|
294
|
+
actorUri: secretaryAgentUri(webId),
|
|
295
|
+
target: sessionUri,
|
|
296
|
+
policyVersion: EXTENSION_UI_POLICY_VERSION,
|
|
112
297
|
};
|
|
113
298
|
}
|
|
114
|
-
function
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
299
|
+
function buildExtensionUiApprovalRequest(input) {
|
|
300
|
+
const prompt = [input.title, input.message].filter(Boolean).join('\n');
|
|
301
|
+
return {
|
|
302
|
+
kind: 'codex-approval',
|
|
303
|
+
message: prompt,
|
|
304
|
+
toolCallId: `extension-ui-${input.kind}-${randomUUID()}`,
|
|
305
|
+
toolName: `extension-ui-${input.kind}`,
|
|
306
|
+
action: 'https://undefineds.co/ns#runtimeApproval',
|
|
307
|
+
risk: 'medium',
|
|
308
|
+
approvalOptions: input.approvalOptions,
|
|
309
|
+
...(input.timeoutMs ? { timeoutMs: input.timeoutMs } : {}),
|
|
310
|
+
context: JSON.stringify({
|
|
311
|
+
source: 'pi-extension-ui',
|
|
312
|
+
kind: input.kind,
|
|
313
|
+
title: input.title,
|
|
314
|
+
...(input.message ? { message: input.message } : {}),
|
|
315
|
+
choices: input.choices,
|
|
316
|
+
...(input.cwd ? { cwd: input.cwd } : {}),
|
|
317
|
+
}),
|
|
318
|
+
entry: input.sessionUri,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function shouldPodBackSelect(choices) {
|
|
322
|
+
const inferred = choices.map((choice) => inferApprovalOptionKind(choice));
|
|
323
|
+
const hasAllow = inferred.some((kind) => kind === 'allow_once' || kind === 'allow_always');
|
|
324
|
+
const hasReject = inferred.some((kind) => kind === 'reject_once' || kind === 'reject_always' || kind === 'cancel');
|
|
325
|
+
return hasAllow && hasReject;
|
|
326
|
+
}
|
|
327
|
+
function buildSelectApprovalOption(label, index) {
|
|
328
|
+
const kind = inferApprovalOptionKind(label);
|
|
329
|
+
return {
|
|
330
|
+
optionId: String(index),
|
|
331
|
+
label,
|
|
332
|
+
...(kind ? { kind } : {}),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function inferApprovalOptionKind(label) {
|
|
336
|
+
const normalized = label.toLowerCase();
|
|
337
|
+
if (/\b(always|session|trust)\b/u.test(normalized)) {
|
|
338
|
+
return 'allow_always';
|
|
339
|
+
}
|
|
340
|
+
if (/\b(allow|approve|yes|ok|confirm|proceed|continue)\b/u.test(normalized)) {
|
|
341
|
+
return 'allow_once';
|
|
342
|
+
}
|
|
343
|
+
if (/\b(cancel|escape)\b/u.test(normalized)) {
|
|
344
|
+
return 'cancel';
|
|
128
345
|
}
|
|
129
|
-
|
|
346
|
+
if (/\b(block|deny|decline|reject|no)\b/u.test(normalized)) {
|
|
347
|
+
return 'reject_once';
|
|
348
|
+
}
|
|
349
|
+
return undefined;
|
|
130
350
|
}
|
|
131
|
-
function
|
|
132
|
-
if (
|
|
133
|
-
return
|
|
134
|
-
mode: input.mode,
|
|
135
|
-
request: input.request,
|
|
136
|
-
});
|
|
351
|
+
function decisionFromSelectedChoice(selected, approvalOptions) {
|
|
352
|
+
if (!selected) {
|
|
353
|
+
return 'cancel';
|
|
137
354
|
}
|
|
138
|
-
|
|
139
|
-
|
|
355
|
+
const option = approvalOptions.find((entry) => entry.label === selected);
|
|
356
|
+
if (option?.kind === 'allow_always') {
|
|
140
357
|
return 'accept_for_session';
|
|
141
358
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
359
|
+
if (option?.kind === 'reject_once' || option?.kind === 'reject_always') {
|
|
360
|
+
return 'decline';
|
|
361
|
+
}
|
|
362
|
+
if (option?.kind === 'cancel') {
|
|
363
|
+
return 'cancel';
|
|
364
|
+
}
|
|
365
|
+
return 'accept';
|
|
146
366
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
367
|
+
function podDecisionFromSelectedChoice(selected, approvalOptions) {
|
|
368
|
+
const decision = decisionFromSelectedChoice(selected, approvalOptions);
|
|
369
|
+
// Extension UI options belong to the extension, not LinX's reusable grant
|
|
370
|
+
// layer. Preserve the exact option in the note without creating a grant.
|
|
371
|
+
return decision === 'accept_for_session' ? 'accept' : decision;
|
|
149
372
|
}
|
|
150
|
-
function
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return null;
|
|
373
|
+
function choiceFromRemoteDecision(decision, choices, approvalOptions) {
|
|
374
|
+
if (decision === 'cancel') {
|
|
375
|
+
return undefined;
|
|
154
376
|
}
|
|
155
|
-
const
|
|
156
|
-
?
|
|
157
|
-
:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
},
|
|
166
|
-
piToolCall: context.toolCall,
|
|
167
|
-
args: context.args,
|
|
168
|
-
};
|
|
169
|
-
if (toolName === 'bash') {
|
|
170
|
-
const args = isRecord(context.args) ? context.args : {};
|
|
171
|
-
const command = typeof args.command === 'string' ? args.command : undefined;
|
|
172
|
-
return {
|
|
173
|
-
kind: 'command-approval',
|
|
174
|
-
message: command ? `Approve command: ${command}` : 'Approve bash command',
|
|
175
|
-
command,
|
|
176
|
-
cwd,
|
|
177
|
-
raw,
|
|
178
|
-
};
|
|
377
|
+
const preferredKinds = decision === 'accept_for_session'
|
|
378
|
+
? ['allow_always', 'allow_once']
|
|
379
|
+
: decision === 'accept'
|
|
380
|
+
? ['allow_once', 'allow_always']
|
|
381
|
+
: ['reject_once', 'reject_always', 'cancel'];
|
|
382
|
+
for (const kind of preferredKinds) {
|
|
383
|
+
const option = approvalOptions.find((entry) => entry.kind === kind);
|
|
384
|
+
if (option) {
|
|
385
|
+
return choices[Number(option.optionId)] ?? option.label;
|
|
386
|
+
}
|
|
179
387
|
}
|
|
180
|
-
if (
|
|
181
|
-
return
|
|
182
|
-
kind: 'file-change-approval',
|
|
183
|
-
message: `Approve ${toolName} tool call`,
|
|
184
|
-
reason: summarizeToolArgs(toolName, context.args),
|
|
185
|
-
raw,
|
|
186
|
-
};
|
|
388
|
+
if (decision === 'decline') {
|
|
389
|
+
return undefined;
|
|
187
390
|
}
|
|
188
|
-
return
|
|
391
|
+
return choices[0];
|
|
189
392
|
}
|
|
190
|
-
function
|
|
191
|
-
const
|
|
192
|
-
return
|
|
193
|
-
id: sessionId,
|
|
194
|
-
backend: 'codex',
|
|
195
|
-
runtime: 'local',
|
|
196
|
-
transport: 'native',
|
|
197
|
-
mode: options.mode ?? 'smart',
|
|
198
|
-
cwd: options.cwd,
|
|
199
|
-
passthroughArgs: [],
|
|
200
|
-
credentialSource: 'cloud',
|
|
201
|
-
resolvedCredentialSource: 'cloud',
|
|
202
|
-
approvalSource: 'hybrid',
|
|
203
|
-
command: 'linx',
|
|
204
|
-
args: [],
|
|
205
|
-
status: 'running',
|
|
206
|
-
startedAt: new Date(context.assistantMessage.timestamp ?? Date.now()).toISOString(),
|
|
207
|
-
archiveDir: '',
|
|
208
|
-
eventsFile: '',
|
|
209
|
-
};
|
|
393
|
+
function optionIdForChoice(selected, choices) {
|
|
394
|
+
const index = choices.findIndex((choice) => choice === selected);
|
|
395
|
+
return index >= 0 ? String(index) : undefined;
|
|
210
396
|
}
|
|
211
|
-
function
|
|
212
|
-
|
|
213
|
-
|
|
397
|
+
function encodeExtensionUiNote(value) {
|
|
398
|
+
return JSON.stringify({
|
|
399
|
+
source: 'pi-extension-ui',
|
|
400
|
+
...value,
|
|
401
|
+
});
|
|
214
402
|
}
|
|
215
|
-
function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
block: true,
|
|
221
|
-
reason: decision === 'cancel'
|
|
222
|
-
? `LinX cancelled ${autoModeApprovalRequestMessage(request)}`
|
|
223
|
-
: `LinX denied ${autoModeApprovalRequestMessage(request)}`,
|
|
224
|
-
};
|
|
403
|
+
function resolveSessionId(value) {
|
|
404
|
+
const resolved = typeof value === 'function' ? value() : value;
|
|
405
|
+
return typeof resolved === 'string' && resolved.trim() ? resolved.trim() : undefined;
|
|
225
406
|
}
|
|
226
|
-
function
|
|
227
|
-
if (!
|
|
228
|
-
return
|
|
407
|
+
function linkAbortSignal(signal, onAbort) {
|
|
408
|
+
if (!signal) {
|
|
409
|
+
return () => undefined;
|
|
229
410
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
237
|
-
}
|
|
238
|
-
export function buildLinxPiApprovalDetails(request) {
|
|
239
|
-
return {
|
|
240
|
-
message: autoModeApprovalRequestMessage(request),
|
|
241
|
-
toolName: autoModeApprovalToolName(request),
|
|
242
|
-
action: autoModeApprovalActionUri(request),
|
|
243
|
-
risk: autoModeApprovalRisk(request),
|
|
244
|
-
};
|
|
411
|
+
if (signal.aborted) {
|
|
412
|
+
onAbort();
|
|
413
|
+
return () => undefined;
|
|
414
|
+
}
|
|
415
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
416
|
+
return () => signal.removeEventListener('abort', onAbort);
|
|
245
417
|
}
|
|
246
418
|
//# sourceMappingURL=pod-approval.js.map
|