create-walle 0.9.20 → 0.9.21
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 +2 -2
- package/package.json +1 -1
- package/template/claude-task-manager/db.js +131 -0
- package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +58 -50
- package/template/claude-task-manager/docs/phone-access-design.md +23 -7
- package/template/claude-task-manager/docs/walle-session-model-preferences.md +119 -0
- package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +32 -48
- package/template/claude-task-manager/lib/remote-relay-protocol.js +5 -0
- package/template/claude-task-manager/lib/walle-external-actions.js +20 -3
- package/template/claude-task-manager/public/index.html +25 -0
- package/template/claude-task-manager/public/js/setup.js +16 -12
- package/template/claude-task-manager/public/js/walle-session.js +31 -3
- package/template/claude-task-manager/public/js/walle.js +93 -23
- package/template/claude-task-manager/public/m/app.css +417 -21
- package/template/claude-task-manager/public/m/app.js +831 -44
- package/template/claude-task-manager/public/m/claim.html +1 -1
- package/template/claude-task-manager/public/m/index.html +41 -7
- package/template/claude-task-manager/public/m/sw.js +1 -1
- package/template/claude-task-manager/server.js +377 -30
- package/template/claude-task-manager/workers/state-detectors/codex.js +18 -3
- package/template/package.json +1 -1
- package/template/wall-e/chat.js +32 -2
- package/template/wall-e/coding/stream-processor.js +36 -0
- package/template/wall-e/coding-orchestrator.js +45 -0
- package/template/wall-e/docs/external-action-controller.md +60 -2
- package/template/wall-e/external-action-controller.js +23 -1
- package/template/wall-e/external-action-gateway.js +163 -0
- package/template/wall-e/fly.toml +1 -0
- package/template/wall-e/tools/local-tools.js +122 -4
- package/template/website/index.html +2 -2
|
@@ -19,7 +19,6 @@ const HOST_READY_TIMEOUT_MS = 45000;
|
|
|
19
19
|
const WATCHDOG_CHECK_INTERVAL_MS = 30000;
|
|
20
20
|
const WATCHDOG_WAKE_DRIFT_MS = 90000;
|
|
21
21
|
const PUBLIC_PROBE_TIMEOUT_MS = 6000;
|
|
22
|
-
const ANONYMOUS_ACCESS_EXPIRATION = '30d';
|
|
23
22
|
const HOST_HEADER_MODE_PASSTHROUGH = 'passthrough';
|
|
24
23
|
const HOST_HEADER_MODE_DEFAULT = 'default';
|
|
25
24
|
|
|
@@ -232,16 +231,8 @@ function devTunnelAccessListArgs(tunnelId, options = {}) {
|
|
|
232
231
|
return ['access', 'list', tunnelId, '-p', String(options.port || 3456), '-j'];
|
|
233
232
|
}
|
|
234
233
|
|
|
235
|
-
function
|
|
236
|
-
return [
|
|
237
|
-
'access',
|
|
238
|
-
'create',
|
|
239
|
-
tunnelId,
|
|
240
|
-
'-p', String(options.port || 3456),
|
|
241
|
-
'--anonymous',
|
|
242
|
-
'--expiration', ANONYMOUS_ACCESS_EXPIRATION,
|
|
243
|
-
'--scopes', 'connect',
|
|
244
|
-
];
|
|
234
|
+
function devTunnelPrivateAccessResetArgs(tunnelId, options = {}) {
|
|
235
|
+
return ['access', 'reset', tunnelId, '-p', String(options.port || 3456), '-j'];
|
|
245
236
|
}
|
|
246
237
|
|
|
247
238
|
function caffeinateArgs(options = {}) {
|
|
@@ -492,13 +483,14 @@ async function annotateWatchdogState(partial, options = {}) {
|
|
|
492
483
|
function managedTunnelAccessSummary(partial = {}, options = {}) {
|
|
493
484
|
const port = Number(options.port || 3456);
|
|
494
485
|
return {
|
|
495
|
-
mode: '
|
|
496
|
-
anonymous_connect:
|
|
486
|
+
mode: 'private_microsoft',
|
|
487
|
+
anonymous_connect: false,
|
|
497
488
|
port,
|
|
498
|
-
expiration: ANONYMOUS_ACCESS_EXPIRATION,
|
|
499
489
|
checked_at: partial.checked_at || new Date().toISOString(),
|
|
500
490
|
already_configured: !!partial.already_configured,
|
|
501
|
-
|
|
491
|
+
reset: !!partial.reset,
|
|
492
|
+
anonymous_removed: !!partial.anonymous_removed,
|
|
493
|
+
list_error: partial.list_error || '',
|
|
502
494
|
error: partial.error || '',
|
|
503
495
|
};
|
|
504
496
|
}
|
|
@@ -508,7 +500,7 @@ function persistManagedTunnelAccess(tunnelId, access, options = {}) {
|
|
|
508
500
|
if (!state || normalizeTunnelId(state.tunnel_id || '') !== normalizeTunnelId(tunnelId || '')) return;
|
|
509
501
|
writeManagedTunnelState({
|
|
510
502
|
...state,
|
|
511
|
-
access_mode: access.mode || '
|
|
503
|
+
access_mode: access.mode || 'private_microsoft',
|
|
512
504
|
access,
|
|
513
505
|
last_access_check_at: access.checked_at || new Date().toISOString(),
|
|
514
506
|
access_error: access.error || '',
|
|
@@ -789,7 +781,7 @@ function microsoftDevTunnelProbeDiagnosis(status, wwwAuthenticate, location) {
|
|
|
789
781
|
diagnosis: 'tunnel_auth_required',
|
|
790
782
|
blocked_by_tunnel_auth: true,
|
|
791
783
|
ctm_reachable: false,
|
|
792
|
-
message: 'Microsoft Dev Tunnels is reachable
|
|
784
|
+
message: 'Microsoft Dev Tunnels is reachable and the private access gate is asking the browser to sign in before CTM receives the request.',
|
|
793
785
|
};
|
|
794
786
|
}
|
|
795
787
|
if (status >= 300 && status < 400 && /(?:tunnels\.api\.visualstudio\.com\/auth|\/auth\/(?:github|microsoft|entra))/i.test(redirect)) {
|
|
@@ -1137,7 +1129,7 @@ function microsoftDevTunnelCommands(options = {}) {
|
|
|
1137
1129
|
login: command('devtunnel', ['user', 'login', '-g']),
|
|
1138
1130
|
device_login: command('devtunnel', ['user', 'login', '-g', '-d']),
|
|
1139
1131
|
microsoft_device_login: command('devtunnel', ['user', 'login', '-e', '-d']),
|
|
1140
|
-
|
|
1132
|
+
access_private_reset: command('devtunnel', devTunnelPrivateAccessResetArgs('TUNNELID', { port })),
|
|
1141
1133
|
host_temporary: command('devtunnel', [
|
|
1142
1134
|
'host',
|
|
1143
1135
|
'-p', String(port),
|
|
@@ -1169,49 +1161,42 @@ function ignoreAlreadyExists(err) {
|
|
|
1169
1161
|
return /already\s+exists|conflict/i.test(text);
|
|
1170
1162
|
}
|
|
1171
1163
|
|
|
1172
|
-
async function
|
|
1164
|
+
async function ensurePrivateConnectAccess(tunnelId, options = {}) {
|
|
1173
1165
|
const checkedAt = new Date().toISOString();
|
|
1174
1166
|
const listArgs = devTunnelAccessListArgs(tunnelId, options);
|
|
1167
|
+
let listError = '';
|
|
1175
1168
|
try {
|
|
1176
1169
|
const list = await execDevTunnel(listArgs, options);
|
|
1177
|
-
|
|
1170
|
+
const hasAnonymous = hasAnonymousConnectAccess(list?.stdout || list?.stderr || '');
|
|
1171
|
+
if (!hasAnonymous) {
|
|
1178
1172
|
const access = managedTunnelAccessSummary({
|
|
1179
|
-
anonymous_connect: true,
|
|
1180
1173
|
already_configured: true,
|
|
1181
1174
|
checked_at: checkedAt,
|
|
1182
1175
|
}, options);
|
|
1183
1176
|
persistManagedTunnelAccess(tunnelId, access, options);
|
|
1184
1177
|
return access;
|
|
1185
1178
|
}
|
|
1186
|
-
} catch {
|
|
1187
|
-
|
|
1188
|
-
//
|
|
1189
|
-
// still the
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
listError = String(err?.stderr || err?.message || err || 'Could not list Microsoft tunnel access.').slice(0, 500);
|
|
1181
|
+
// Older CLI builds and transient service errors can make listing fail. The
|
|
1182
|
+
// reset below is still safe: it returns the port to Dev Tunnels defaults.
|
|
1190
1183
|
}
|
|
1191
1184
|
|
|
1192
1185
|
try {
|
|
1193
|
-
await execDevTunnel(
|
|
1186
|
+
await execDevTunnel(devTunnelPrivateAccessResetArgs(tunnelId, options), options);
|
|
1194
1187
|
const access = managedTunnelAccessSummary({
|
|
1195
|
-
|
|
1196
|
-
|
|
1188
|
+
reset: true,
|
|
1189
|
+
anonymous_removed: !listError,
|
|
1190
|
+
list_error: listError,
|
|
1197
1191
|
checked_at: checkedAt,
|
|
1198
1192
|
}, options);
|
|
1199
1193
|
persistManagedTunnelAccess(tunnelId, access, options);
|
|
1200
1194
|
return access;
|
|
1201
1195
|
} catch (err) {
|
|
1202
|
-
|
|
1203
|
-
const access = managedTunnelAccessSummary({
|
|
1204
|
-
anonymous_connect: true,
|
|
1205
|
-
already_configured: true,
|
|
1206
|
-
checked_at: checkedAt,
|
|
1207
|
-
}, options);
|
|
1208
|
-
persistManagedTunnelAccess(tunnelId, access, options);
|
|
1209
|
-
return access;
|
|
1210
|
-
}
|
|
1211
|
-
const message = String(err?.stderr || err?.message || err || 'Could not enable anonymous Microsoft tunnel access.').slice(0, 800);
|
|
1196
|
+
const message = String(err?.stderr || err?.message || err || 'Could not reset Microsoft tunnel access to private.').slice(0, 800);
|
|
1212
1197
|
const access = managedTunnelAccessSummary({
|
|
1213
|
-
anonymous_connect: false,
|
|
1214
1198
|
checked_at: checkedAt,
|
|
1199
|
+
list_error: listError,
|
|
1215
1200
|
error: message,
|
|
1216
1201
|
}, options);
|
|
1217
1202
|
persistManagedTunnelAccess(tunnelId, access, options);
|
|
@@ -1236,7 +1221,7 @@ async function ensurePersistentTunnel(tunnelId, options = {}) {
|
|
|
1236
1221
|
}
|
|
1237
1222
|
}
|
|
1238
1223
|
}
|
|
1239
|
-
const access = await
|
|
1224
|
+
const access = await ensurePrivateConnectAccess(tunnelId, options);
|
|
1240
1225
|
return { access };
|
|
1241
1226
|
}
|
|
1242
1227
|
|
|
@@ -1383,7 +1368,7 @@ async function applyMicrosoftDevTunnelSetup(input = {}, options = {}) {
|
|
|
1383
1368
|
if (previousProcess && managedTunnelProcessUsable(previousProcess, previous) && previous.origin) {
|
|
1384
1369
|
let access = null;
|
|
1385
1370
|
try {
|
|
1386
|
-
access = await
|
|
1371
|
+
access = await ensurePrivateConnectAccess(previous.tunnel_id, options);
|
|
1387
1372
|
} catch (err) {
|
|
1388
1373
|
return {
|
|
1389
1374
|
ok: false,
|
|
@@ -1445,7 +1430,7 @@ async function restoreMicrosoftDevTunnelHost(options = {}) {
|
|
|
1445
1430
|
} else if (managedTunnelProcessUsable(managed, state)) {
|
|
1446
1431
|
let access = managed.access || null;
|
|
1447
1432
|
if (options.repairAccess === true) {
|
|
1448
|
-
access = await
|
|
1433
|
+
access = await ensurePrivateConnectAccess(state.tunnel_id, options);
|
|
1449
1434
|
}
|
|
1450
1435
|
const latest = await detectManagedMicrosoftDevTunnel(options);
|
|
1451
1436
|
return {
|
|
@@ -1538,7 +1523,7 @@ async function checkMicrosoftDevTunnelAvailability(options = {}) {
|
|
|
1538
1523
|
let managed = before;
|
|
1539
1524
|
if (options.repairAccess === true) {
|
|
1540
1525
|
try {
|
|
1541
|
-
const access = await
|
|
1526
|
+
const access = await ensurePrivateConnectAccess(state.tunnel_id, options);
|
|
1542
1527
|
const latest = await detectManagedMicrosoftDevTunnel(options);
|
|
1543
1528
|
managed = { ...latest, access, access_mode: access.mode, last_access_check_at: access.checked_at, access_error: access.error || '' };
|
|
1544
1529
|
} catch (err) {
|
|
@@ -1572,14 +1557,13 @@ async function checkMicrosoftDevTunnelAvailability(options = {}) {
|
|
|
1572
1557
|
};
|
|
1573
1558
|
}
|
|
1574
1559
|
if (probe.blocked_by_tunnel_auth) {
|
|
1575
|
-
|
|
1576
|
-
await annotateWatchdogState({ last_watchdog_check_at: checkedAt, watchdog_error: error }, options);
|
|
1560
|
+
await annotateWatchdogState({ last_watchdog_check_at: checkedAt, watchdog_error: '' }, options);
|
|
1577
1561
|
return {
|
|
1578
|
-
ok:
|
|
1562
|
+
ok: true,
|
|
1579
1563
|
checked: true,
|
|
1580
1564
|
recovered: false,
|
|
1581
|
-
reason: '
|
|
1582
|
-
|
|
1565
|
+
reason: 'private_tunnel_auth_required',
|
|
1566
|
+
public_probe: probe,
|
|
1583
1567
|
keep_awake: await getMicrosoftDevTunnelKeepAwakeStatus(options),
|
|
1584
1568
|
managed_tunnel: managed,
|
|
1585
1569
|
};
|
|
@@ -15,6 +15,7 @@ const MESSAGE_REGISTRY = Object.freeze({
|
|
|
15
15
|
'session.cancel_stream': rule('respond', 'medium', false),
|
|
16
16
|
'session.prompt_history': rule('respond', 'medium', false),
|
|
17
17
|
'wall_e.send_message': rule('respond', 'low', false),
|
|
18
|
+
'wall_e.set_model': rule('respond', 'low', false),
|
|
18
19
|
|
|
19
20
|
'approval.respond': rule('respond', 'high', true, { enabled: false }),
|
|
20
21
|
'session.kill': rule('admin', 'high', true, { enabled: false }),
|
|
@@ -139,6 +140,10 @@ async function dispatchMessage(message, context = {}) {
|
|
|
139
140
|
return handlers.sendWalleMessage
|
|
140
141
|
? handlers.sendWalleMessage(sessionIdFromBody(message.body), String(message.body.text || message.body.message || ''), message.body)
|
|
141
142
|
: safeError('handler_missing');
|
|
143
|
+
case 'wall_e.set_model':
|
|
144
|
+
return handlers.setWalleModel
|
|
145
|
+
? handlers.setWalleModel(sessionIdFromBody(message.body), message.body)
|
|
146
|
+
: safeError('handler_missing');
|
|
142
147
|
default:
|
|
143
148
|
return safeError('remote_action_not_enabled');
|
|
144
149
|
}
|
|
@@ -208,9 +208,10 @@ function isSuccessfulExternalActionResult(toolCall) {
|
|
|
208
208
|
if (parsed?.external_action === true && parsed?.blocked === true) return false;
|
|
209
209
|
if (parsed && (parsed.error || parsed.ok === false || parsed.success === false)) return false;
|
|
210
210
|
if (!SIDE_EFFECT_TOOL_NAMES.has(name)) return false;
|
|
211
|
-
if (!parsed) return /approved action executed/i.test(toolCall.summary || '');
|
|
212
|
-
if (parsed.
|
|
213
|
-
|
|
211
|
+
if (!parsed) return /approved action (?:was already executed|executed and verified)/i.test(toolCall.summary || '');
|
|
212
|
+
if (parsed.alreadyExecuted === true) return true;
|
|
213
|
+
if (parsed.verified === true || parsed.verification?.ok === true) return true;
|
|
214
|
+
return /approved action (?:was already executed|executed and verified)/i.test(toolCall.summary || '');
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
function hasExecutedExternalActionToolResult(toolCalls = []) {
|
|
@@ -218,6 +219,14 @@ function hasExecutedExternalActionToolResult(toolCalls = []) {
|
|
|
218
219
|
return toolCalls.some(isSuccessfulExternalActionResult);
|
|
219
220
|
}
|
|
220
221
|
|
|
222
|
+
function hasBlockedExternalActionGatewayToolResult(toolCalls = []) {
|
|
223
|
+
if (!Array.isArray(toolCalls)) return false;
|
|
224
|
+
return toolCalls.some((toolCall) => {
|
|
225
|
+
const parsed = parseToolResultPayload(toolCall);
|
|
226
|
+
return parsed?.external_action_gateway === true && parsed?.blocked === true;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
221
230
|
function externalActionCompletionClaim(text) {
|
|
222
231
|
const value = String(text || '').trim();
|
|
223
232
|
if (!value || NEGATED_COMPLETION_RE.test(value)) return false;
|
|
@@ -253,6 +262,13 @@ function externalActionLabel(action = {}) {
|
|
|
253
262
|
|
|
254
263
|
function buildPendingExternalActionNotExecutedReply({ actions = [] } = {}) {
|
|
255
264
|
const pending = Array.isArray(actions) ? actions : [];
|
|
265
|
+
if (!pending.length) {
|
|
266
|
+
return [
|
|
267
|
+
'I do not have verified evidence that those external actions completed.',
|
|
268
|
+
'Wall-E now requires external side effects to go through the dedicated action tools and read-after-write verification before it can summarize success.',
|
|
269
|
+
'Please retry with the dedicated mail/calendar action path, or ask me to stage the action again.',
|
|
270
|
+
].join('\n');
|
|
271
|
+
}
|
|
256
272
|
const lines = [
|
|
257
273
|
'I did not execute those external actions.',
|
|
258
274
|
'The previous turn still has pending approval requests:',
|
|
@@ -271,6 +287,7 @@ module.exports = {
|
|
|
271
287
|
extractLatestPendingExternalActions,
|
|
272
288
|
externalActionCompletionClaim,
|
|
273
289
|
externalActionFromToolCall,
|
|
290
|
+
hasBlockedExternalActionGatewayToolResult,
|
|
274
291
|
hasExecutedExternalActionToolResult,
|
|
275
292
|
isExternalActionConfirmationText,
|
|
276
293
|
};
|
|
@@ -3585,6 +3585,24 @@
|
|
|
3585
3585
|
justify-content: space-between;
|
|
3586
3586
|
}
|
|
3587
3587
|
.standup-attention.active { display: flex; }
|
|
3588
|
+
.standup-service-alerts {
|
|
3589
|
+
display: none;
|
|
3590
|
+
margin: 0;
|
|
3591
|
+
border-color: rgba(224, 175, 104, 0.35);
|
|
3592
|
+
background: rgba(224, 175, 104, 0.08);
|
|
3593
|
+
}
|
|
3594
|
+
.standup-service-alerts.active {
|
|
3595
|
+
display: block;
|
|
3596
|
+
}
|
|
3597
|
+
.standup-service-alerts .we-service-alerts-title {
|
|
3598
|
+
color: var(--yellow);
|
|
3599
|
+
}
|
|
3600
|
+
.standup-service-alerts .we-service-alert-item {
|
|
3601
|
+
padding: 6px 0;
|
|
3602
|
+
}
|
|
3603
|
+
.standup-service-alerts .we-service-alert-text {
|
|
3604
|
+
color: var(--fg);
|
|
3605
|
+
}
|
|
3588
3606
|
.standup-attention-main {
|
|
3589
3607
|
min-width: 0;
|
|
3590
3608
|
display: flex;
|
|
@@ -3898,6 +3916,9 @@
|
|
|
3898
3916
|
min-width: 0;
|
|
3899
3917
|
white-space: normal;
|
|
3900
3918
|
}
|
|
3919
|
+
.standup-service-alerts .we-service-alert-item {
|
|
3920
|
+
align-items: flex-start;
|
|
3921
|
+
}
|
|
3901
3922
|
.standup-attention { align-items: flex-start; flex-direction: column; }
|
|
3902
3923
|
.standup-attention-actions { width: 100%; justify-content: flex-end; }
|
|
3903
3924
|
}
|
|
@@ -5187,6 +5208,7 @@
|
|
|
5187
5208
|
</div>
|
|
5188
5209
|
<div class="standup-search-status" id="standup-search-status" aria-live="polite"></div>
|
|
5189
5210
|
</div>
|
|
5211
|
+
<div class="standup-service-alerts" id="standup-service-alerts"></div>
|
|
5190
5212
|
<div class="standup-attention" id="standup-attention"></div>
|
|
5191
5213
|
<div class="standup-loading" id="standup-loading">Loading sessions...</div>
|
|
5192
5214
|
<div class="standup-error" id="standup-error" style="display:none;"></div>
|
|
@@ -19443,6 +19465,9 @@ function onCreated(msg) {
|
|
|
19443
19465
|
agentType: 'walle',
|
|
19444
19466
|
model_id: msg.model_id || null,
|
|
19445
19467
|
model_provider: msg.model_provider || null,
|
|
19468
|
+
model_registry_id: msg.model_registry_id || msg.modelRegistryId || '',
|
|
19469
|
+
model_provider_id: msg.model_provider_id || msg.modelProviderId || '',
|
|
19470
|
+
model_pinned: msg.model_pinned === true || msg.modelPinned === true,
|
|
19446
19471
|
},
|
|
19447
19472
|
walleState: null,
|
|
19448
19473
|
};
|
|
@@ -2094,7 +2094,10 @@ function _microsoftAvailabilityCopy(ms, ready) {
|
|
|
2094
2094
|
}
|
|
2095
2095
|
|
|
2096
2096
|
function _microsoftTunnelPhoneAuthText(ms) {
|
|
2097
|
-
|
|
2097
|
+
var account = _microsoftTunnelAccountText(ms);
|
|
2098
|
+
return account
|
|
2099
|
+
? 'If Microsoft asks you to sign in, use this same account: ' + account + '. CTM still requires the pairing QR and device permissions.'
|
|
2100
|
+
: 'If Microsoft asks you to sign in, use the same Microsoft or GitHub account shown on this Mac. CTM still requires the pairing QR and device permissions.';
|
|
2098
2101
|
}
|
|
2099
2102
|
|
|
2100
2103
|
function _microsoftTunnelOrigin(d) {
|
|
@@ -2482,15 +2485,15 @@ function _renderMicrosoftTunnelTraffic(traffic) {
|
|
|
2482
2485
|
function _microsoftProbeTone(probe) {
|
|
2483
2486
|
if (!probe || !probe.checked) return '';
|
|
2484
2487
|
if (probe.ctm_reachable) return 'status-ok';
|
|
2485
|
-
if (probe.blocked_by_tunnel_auth || probe.diagnosis === 'tunnel_auth_required') return 'status-
|
|
2488
|
+
if (probe.blocked_by_tunnel_auth || probe.diagnosis === 'tunnel_auth_required') return 'status-ok';
|
|
2486
2489
|
return 'status-error';
|
|
2487
2490
|
}
|
|
2488
2491
|
|
|
2489
2492
|
function _microsoftProbeStatusText(probe, ms) {
|
|
2490
|
-
if (_microsoftProbeInFlight) return 'Checking the
|
|
2493
|
+
if (_microsoftProbeInFlight) return 'Checking the private phone URL...';
|
|
2491
2494
|
if (!probe || !probe.checked) return 'Run this when the phone cannot load the tunnel URL.';
|
|
2492
2495
|
if (probe.ctm_reachable) return 'CTM is reachable through the tunnel from this Mac.';
|
|
2493
|
-
if (probe.blocked_by_tunnel_auth || probe.diagnosis === 'tunnel_auth_required') return 'Microsoft
|
|
2496
|
+
if (probe.blocked_by_tunnel_auth || probe.diagnosis === 'tunnel_auth_required') return 'Private Microsoft sign-in is active before CTM.';
|
|
2494
2497
|
if (probe.diagnosis === 'timeout') return 'The tunnel URL timed out before CTM responded.';
|
|
2495
2498
|
if (probe.diagnosis === 'not_configured') return 'Start the tunnel before checking the phone URL.';
|
|
2496
2499
|
return probe.status ? ('Tunnel check returned HTTP ' + probe.status + '.') : 'The tunnel URL is not reachable from this Mac.';
|
|
@@ -2499,15 +2502,15 @@ function _microsoftProbeStatusText(probe, ms) {
|
|
|
2499
2502
|
function _microsoftProbeNoteText(probe, ms) {
|
|
2500
2503
|
if (_microsoftProbeInFlight) return 'CTM is checking the same devtunnels.ms URL your phone opens.';
|
|
2501
2504
|
if (!probe || !probe.checked) {
|
|
2502
|
-
return 'If this reports a Microsoft gate,
|
|
2505
|
+
return 'If this reports a Microsoft gate, that is expected for a private tunnel. On the phone, sign in with the same account shown above.';
|
|
2503
2506
|
}
|
|
2504
2507
|
if (probe.ctm_reachable) {
|
|
2505
2508
|
return 'The Mac-side check reached CTM. If the phone still fails, refresh the phone page and check the CTM traffic list below.';
|
|
2506
2509
|
}
|
|
2507
2510
|
if (probe.blocked_by_tunnel_auth || probe.diagnosis === 'tunnel_auth_required') {
|
|
2508
|
-
return '
|
|
2511
|
+
return 'The tunnel is private. This Mac-side check is stopped by Microsoft because it is not a signed-in browser session; your phone should sign in with the same Microsoft/GitHub account, then CTM will load.';
|
|
2509
2512
|
}
|
|
2510
|
-
return probe.message || 'Use Recover Now if the tunnel should be running, then check the URL again.';
|
|
2513
|
+
return probe.message || 'Use Recover Now if the tunnel process should be running, then check the URL again.';
|
|
2511
2514
|
}
|
|
2512
2515
|
|
|
2513
2516
|
function _renderMicrosoftTunnelProbe(probe, ms, ready) {
|
|
@@ -2564,9 +2567,9 @@ function _renderMicrosoftDevTunnelSetup(ms, d) {
|
|
|
2564
2567
|
} else if (!signedIn) {
|
|
2565
2568
|
summary.textContent = 'Sign in once with GitHub on this Mac. After sign-in finishes, CTM starts the tunnel and creates the phone pairing QR.';
|
|
2566
2569
|
} else if (ready) {
|
|
2567
|
-
summary.textContent = 'Microsoft tunnel is running. Open the phone URL in
|
|
2570
|
+
summary.textContent = 'Private Microsoft tunnel is running. Open the phone URL, sign in with the same account if asked, then pair with CTM.';
|
|
2568
2571
|
} else {
|
|
2569
|
-
summary.textContent = 'CTM can start the tunnel now and create the phone pairing QR.';
|
|
2572
|
+
summary.textContent = 'CTM can start the private tunnel now, remove stale anonymous access, and create the phone pairing QR.';
|
|
2570
2573
|
}
|
|
2571
2574
|
}
|
|
2572
2575
|
if (account) {
|
|
@@ -2622,8 +2625,8 @@ function _renderMicrosoftDevTunnelSetup(ms, d) {
|
|
|
2622
2625
|
}
|
|
2623
2626
|
if (securityNote) {
|
|
2624
2627
|
securityNote.textContent = ready
|
|
2625
|
-
? '
|
|
2626
|
-
: 'Set Up
|
|
2628
|
+
? 'Private Microsoft access is on. CTM also requires the pairing QR, device token, route permissions, and passkey step-up for high-risk actions.'
|
|
2629
|
+
: 'Set Up keeps anonymous access off and resets stale public access before the tunnel is marked ready. Do not share the pairing QR or claim link.';
|
|
2627
2630
|
}
|
|
2628
2631
|
_renderMicrosoftTunnelTraffic(ms.traffic || {});
|
|
2629
2632
|
_renderMicrosoftTunnelProbe(_microsoftTunnelProbe || ms.public_probe || null, ms, ready);
|
|
@@ -3333,6 +3336,7 @@ async function startMicrosoftTunnel() {
|
|
|
3333
3336
|
installed: true,
|
|
3334
3337
|
available: true,
|
|
3335
3338
|
signed_in: true,
|
|
3339
|
+
account: updatedNetwork.microsoft_dev_tunnel && updatedNetwork.microsoft_dev_tunnel.account || {},
|
|
3336
3340
|
origin: values.origin,
|
|
3337
3341
|
mobile_url: values.mobile_url,
|
|
3338
3342
|
inspect_url: values.inspect_url,
|
|
@@ -3451,7 +3455,7 @@ async function probeMicrosoftTunnel() {
|
|
|
3451
3455
|
}
|
|
3452
3456
|
_renderMicrosoftDevTunnelSetup((_lastNetworkSettings || {}).microsoft_dev_tunnel || ms, _lastNetworkSettings || latest);
|
|
3453
3457
|
if (probe.blocked_by_tunnel_auth || probe.diagnosis === 'tunnel_auth_required') {
|
|
3454
|
-
_setMicrosoftActionStatus('Microsoft
|
|
3458
|
+
_setMicrosoftActionStatus('Private Microsoft sign-in is active before CTM. On the phone, sign in with the same Microsoft/GitHub account shown here.', 'ok');
|
|
3455
3459
|
} else if (probe.ctm_reachable) {
|
|
3456
3460
|
_setMicrosoftActionStatus('Phone URL reached CTM from this Mac.', 'ok');
|
|
3457
3461
|
} else {
|
|
@@ -40,12 +40,13 @@ window.WalleSession = (function() {
|
|
|
40
40
|
// ---------- state helper ----------
|
|
41
41
|
function initialModelFromSession(s) {
|
|
42
42
|
var meta = (s && s.meta) || {};
|
|
43
|
-
var model = meta.
|
|
43
|
+
var model = meta.model_id || meta.model || '';
|
|
44
44
|
return {
|
|
45
45
|
model: model,
|
|
46
46
|
registryId: meta.model_registry_id || meta.modelRegistryId || '',
|
|
47
47
|
providerType: meta.model_provider || '',
|
|
48
48
|
provider: meta.model_provider ? providerLabel(meta.model_provider) : '',
|
|
49
|
+
pinned: meta.model_pinned === true || meta.modelPinned === true,
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -67,7 +68,7 @@ window.WalleSession = (function() {
|
|
|
67
68
|
selectedModelProvider: initialModel.provider,
|
|
68
69
|
_currentAssistant: null,
|
|
69
70
|
_modelHydrated: true,
|
|
70
|
-
_modelManual:
|
|
71
|
+
_modelManual: !!initialModel.pinned,
|
|
71
72
|
promptNavIdx: -1,
|
|
72
73
|
inputHistory: [],
|
|
73
74
|
inputHistoryIdx: -1,
|
|
@@ -98,6 +99,9 @@ window.WalleSession = (function() {
|
|
|
98
99
|
if (typeof s.walleState.isPreparingLocation !== 'boolean') s.walleState.isPreparingLocation = false;
|
|
99
100
|
if (typeof s.walleState.selectedModelRegistryId !== 'string') s.walleState.selectedModelRegistryId = '';
|
|
100
101
|
if (typeof s.walleState.selectedModelProviderType !== 'string') s.walleState.selectedModelProviderType = '';
|
|
102
|
+
if ((s.meta && (s.meta.model_pinned === true || s.meta.modelPinned === true)) && s.walleState.selectedModel) {
|
|
103
|
+
s.walleState._modelManual = true;
|
|
104
|
+
}
|
|
101
105
|
if (s.walleState.composerHeightPx != null && (!isFinite(Number(s.walleState.composerHeightPx)) || Number(s.walleState.composerHeightPx) <= 0)) {
|
|
102
106
|
s.walleState.composerHeightPx = null;
|
|
103
107
|
}
|
|
@@ -3008,6 +3012,7 @@ window.WalleSession = (function() {
|
|
|
3008
3012
|
label: label,
|
|
3009
3013
|
baseLabel: stripModelAlias(label),
|
|
3010
3014
|
provider: provider,
|
|
3015
|
+
providerId: m.provider_id || m.providerId || '',
|
|
3011
3016
|
providerLabel: m.provider_name || providerLabel(provider),
|
|
3012
3017
|
capabilities: normalizeModelCapabilities(m.capabilities),
|
|
3013
3018
|
source: m.source || 'live',
|
|
@@ -3159,6 +3164,19 @@ window.WalleSession = (function() {
|
|
|
3159
3164
|
ws.selectedModelProvider = item ? item.providerLabel : '';
|
|
3160
3165
|
ws._modelManual = true;
|
|
3161
3166
|
syncWalleModelButtons(id);
|
|
3167
|
+
if (item && typeof send === 'function') {
|
|
3168
|
+
send({
|
|
3169
|
+
type: 'model-change',
|
|
3170
|
+
id: id,
|
|
3171
|
+
agent_type: 'walle',
|
|
3172
|
+
model_id: item.modelId,
|
|
3173
|
+
model_provider: item.provider,
|
|
3174
|
+
model_registry_id: item.id || '',
|
|
3175
|
+
model_provider_id: item.providerId || '',
|
|
3176
|
+
scope: 'session',
|
|
3177
|
+
pinned: true,
|
|
3178
|
+
});
|
|
3179
|
+
}
|
|
3162
3180
|
closeModelPicker();
|
|
3163
3181
|
}
|
|
3164
3182
|
|
|
@@ -3671,13 +3689,23 @@ window.WalleSession = (function() {
|
|
|
3671
3689
|
s.meta = s.meta || {};
|
|
3672
3690
|
if (msg.model_id) s.meta.model_id = msg.model_id;
|
|
3673
3691
|
if (msg.model_provider) s.meta.model_provider = msg.model_provider;
|
|
3692
|
+
if (msg.model_registry_id || msg.modelRegistryId) s.meta.model_registry_id = msg.model_registry_id || msg.modelRegistryId;
|
|
3693
|
+
if (msg.model_provider_id || msg.modelProviderId) s.meta.model_provider_id = msg.model_provider_id || msg.modelProviderId;
|
|
3694
|
+
if (typeof msg.model_pinned === 'boolean' || typeof msg.modelPinned === 'boolean') {
|
|
3695
|
+
s.meta.model_pinned = msg.model_pinned === true || msg.modelPinned === true;
|
|
3696
|
+
}
|
|
3674
3697
|
var ws = getState(id);
|
|
3675
|
-
if (!ws ||
|
|
3698
|
+
if (!ws || !msg.model_id) return;
|
|
3699
|
+
var sameManualModel = ws._modelManual
|
|
3700
|
+
&& ws.selectedModel === msg.model_id
|
|
3701
|
+
&& (!msg.model_provider || ws.selectedModelProviderType === msg.model_provider);
|
|
3702
|
+
if (ws._modelManual && !sameManualModel) return;
|
|
3676
3703
|
ws.selectedModel = msg.model_id;
|
|
3677
3704
|
ws.selectedModelRegistryId = msg.model_registry_id || msg.modelRegistryId || '';
|
|
3678
3705
|
ws.selectedModelLabel = '';
|
|
3679
3706
|
ws.selectedModelProviderType = msg.model_provider || ws.selectedModelProviderType || '';
|
|
3680
3707
|
ws.selectedModelProvider = msg.model_provider ? providerLabel(msg.model_provider) : ws.selectedModelProvider;
|
|
3708
|
+
if (msg.model_pinned === true || msg.modelPinned === true) ws._modelManual = true;
|
|
3681
3709
|
syncWalleModelButtons(id);
|
|
3682
3710
|
}
|
|
3683
3711
|
|
|
@@ -6489,35 +6489,105 @@ function checkServiceAlerts() {
|
|
|
6489
6489
|
});
|
|
6490
6490
|
}
|
|
6491
6491
|
|
|
6492
|
+
function serviceAlertPresentation(alert) {
|
|
6493
|
+
var providerIssue = _normalizeProviderIssue(alert);
|
|
6494
|
+
var kind = providerIssue ? 'provider' : (alert.type === 'auth_expired' ? 'error' : alert.type === 'update_available' ? 'info' : 'warning');
|
|
6495
|
+
return {
|
|
6496
|
+
providerIssue: providerIssue,
|
|
6497
|
+
kind: kind,
|
|
6498
|
+
icon: kind === 'provider' || kind === 'error' ? '!' : kind === 'info' ? '↑' : '●',
|
|
6499
|
+
message: providerIssue ? providerIssue.message : (alert.message || ''),
|
|
6500
|
+
};
|
|
6501
|
+
}
|
|
6502
|
+
|
|
6503
|
+
function safeServiceAlertId(alert) {
|
|
6504
|
+
return esc(alert && alert.id != null ? alert.id : '').replace(/'/g, ''');
|
|
6505
|
+
}
|
|
6506
|
+
|
|
6507
|
+
function serviceAlertActionHtml(alert, safeId) {
|
|
6508
|
+
var actionBtn = '';
|
|
6509
|
+
var actionLabel = esc(alert.action_label || 'Fix');
|
|
6510
|
+
if (alert.action === 'repair_slack_owner') {
|
|
6511
|
+
var actionName = esc(alert.action || 'custom').replace(/'/g, ''');
|
|
6512
|
+
actionBtn = ' <button class="we-service-alert-action" type="button" onclick="WE._runAlertAction(\'' + safeId + '\', \'' + actionName + '\')">' + actionLabel + '</button>';
|
|
6513
|
+
} else if (alert.action === 'gws_reauth') {
|
|
6514
|
+
actionBtn = ' <button class="we-service-alert-action" type="button" onclick="WE._reconnectGwsAlert(\'' + safeId + '\')">' + actionLabel + '</button>';
|
|
6515
|
+
} else if (alert.action === 'show_update_wizard') {
|
|
6516
|
+
actionBtn = ' <button class="we-service-alert-action" type="button" onclick="WE._showUpdateWizardFromAlert(\'' + safeId + '\')">' + actionLabel + '</button>';
|
|
6517
|
+
} else if (alert.action_url && /^(\/|https?:\/\/)/.test(alert.action_url)) {
|
|
6518
|
+
actionBtn = ' <a href="' + esc(alert.action_url) + '" class="we-service-alert-action">' + actionLabel + '</a>';
|
|
6519
|
+
}
|
|
6520
|
+
return actionBtn;
|
|
6521
|
+
}
|
|
6522
|
+
|
|
6523
|
+
function serviceAlertItemHtml(alert) {
|
|
6524
|
+
var presentation = serviceAlertPresentation(alert);
|
|
6525
|
+
var safeId = safeServiceAlertId(alert);
|
|
6526
|
+
var dismissBtn = '<button class="we-service-alert-dismiss" type="button" onclick="WE._dismissAlert(\'' + safeId + '\')" title="Dismiss">×</button>';
|
|
6527
|
+
return '<div class="we-service-alert-item ' + presentation.kind + '">'
|
|
6528
|
+
+ '<span class="we-service-alert-icon">' + presentation.icon + '</span>'
|
|
6529
|
+
+ '<span class="we-service-alert-text">' + esc(presentation.message) + serviceAlertActionHtml(alert, safeId) + '</span>'
|
|
6530
|
+
+ dismissBtn + '</div>';
|
|
6531
|
+
}
|
|
6532
|
+
|
|
6533
|
+
function serviceAlertSearchText(alert) {
|
|
6534
|
+
return [
|
|
6535
|
+
alert && alert.id,
|
|
6536
|
+
alert && alert.type,
|
|
6537
|
+
alert && alert.service,
|
|
6538
|
+
alert && alert.provider,
|
|
6539
|
+
alert && alert.integration,
|
|
6540
|
+
alert && alert.action,
|
|
6541
|
+
alert && alert.message,
|
|
6542
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
6543
|
+
}
|
|
6544
|
+
|
|
6545
|
+
function isSessionsIntegrationAlert(alert) {
|
|
6546
|
+
if (!alert || alert.type === 'update_available') return false;
|
|
6547
|
+
var action = String(alert.action || '').toLowerCase();
|
|
6548
|
+
var type = String(alert.type || '').toLowerCase();
|
|
6549
|
+
if (action === 'gws_reauth' || action === 'repair_slack_owner') return true;
|
|
6550
|
+
if (type === 'auth_expired') return true;
|
|
6551
|
+
var text = serviceAlertSearchText(alert);
|
|
6552
|
+
var namesIntegration = /(slack|gws|google|gmail|calendar|drive|oauth)/.test(text);
|
|
6553
|
+
var needsAuth = /(auth|reauth|reconnect|expired|token|owner_identity_missing)/.test(text);
|
|
6554
|
+
return namesIntegration && needsAuth;
|
|
6555
|
+
}
|
|
6556
|
+
|
|
6557
|
+
function ensureStandupServiceAlertsContainer() {
|
|
6558
|
+
var existing = document.getElementById('standup-service-alerts');
|
|
6559
|
+
if (existing) return existing;
|
|
6560
|
+
var search = document.getElementById('standup-search');
|
|
6561
|
+
if (!search || !search.parentNode) return null;
|
|
6562
|
+
var node = document.createElement('div');
|
|
6563
|
+
node.id = 'standup-service-alerts';
|
|
6564
|
+
node.className = 'standup-service-alerts';
|
|
6565
|
+
search.parentNode.insertBefore(node, search.nextSibling);
|
|
6566
|
+
return node;
|
|
6567
|
+
}
|
|
6568
|
+
|
|
6569
|
+
function renderStandupServiceAlerts(alerts) {
|
|
6570
|
+
var container = ensureStandupServiceAlertsContainer();
|
|
6571
|
+
if (!container) return;
|
|
6572
|
+
var integrationAlerts = (alerts || []).filter(isSessionsIntegrationAlert);
|
|
6573
|
+
if (!integrationAlerts.length) {
|
|
6574
|
+
container.className = 'standup-service-alerts';
|
|
6575
|
+
container.innerHTML = '';
|
|
6576
|
+
return;
|
|
6577
|
+
}
|
|
6578
|
+
container.className = 'standup-service-alerts active we-service-alerts';
|
|
6579
|
+
container.innerHTML = '<div class="we-service-alerts-title">Integration Alerts</div>'
|
|
6580
|
+
+ integrationAlerts.map(serviceAlertItemHtml).join('');
|
|
6581
|
+
}
|
|
6582
|
+
|
|
6492
6583
|
function renderServiceAlerts(alerts) {
|
|
6493
6584
|
var existing = document.getElementById('walle-service-alerts');
|
|
6585
|
+
renderStandupServiceAlerts(alerts);
|
|
6494
6586
|
if (!alerts || alerts.length === 0) {
|
|
6495
6587
|
if (existing) existing.remove();
|
|
6496
6588
|
return;
|
|
6497
6589
|
}
|
|
6498
|
-
var items = alerts.map(
|
|
6499
|
-
var providerIssue = _normalizeProviderIssue(a);
|
|
6500
|
-
var kind = providerIssue ? 'provider' : (a.type === 'auth_expired' ? 'error' : a.type === 'update_available' ? 'info' : 'warning');
|
|
6501
|
-
var icon = kind === 'provider' || kind === 'error' ? '!' : kind === 'info' ? '↑' : '●';
|
|
6502
|
-
var safeId = esc(a.id).replace(/'/g, ''');
|
|
6503
|
-
var dismissBtn = '<button class="we-service-alert-dismiss" onclick="WE._dismissAlert(\'' + safeId + '\')" title="Dismiss">×</button>';
|
|
6504
|
-
var actionBtn = '';
|
|
6505
|
-
var actionLabel = esc(a.action_label || 'Fix');
|
|
6506
|
-
if (a.action === 'repair_slack_owner') {
|
|
6507
|
-
var actionName = esc(a.action || 'custom').replace(/'/g, ''');
|
|
6508
|
-
actionBtn = ' <button class="we-service-alert-action" onclick="WE._runAlertAction(\'' + safeId + '\', \'' + actionName + '\')">' + actionLabel + '</button>';
|
|
6509
|
-
} else if (a.action === 'gws_reauth') {
|
|
6510
|
-
actionBtn = ' <button class="we-service-alert-action" onclick="WE._reconnectGwsAlert(\'' + safeId + '\')">' + actionLabel + '</button>';
|
|
6511
|
-
} else if (a.action === 'show_update_wizard') {
|
|
6512
|
-
actionBtn = ' <button class="we-service-alert-action" onclick="WE._showUpdateWizardFromAlert(\'' + safeId + '\')">' + actionLabel + '</button>';
|
|
6513
|
-
} else if (a.action_url && /^(\/|https?:\/\/)/.test(a.action_url)) {
|
|
6514
|
-
actionBtn = ' <a href="' + esc(a.action_url) + '" class="we-service-alert-action">' + actionLabel + '</a>';
|
|
6515
|
-
}
|
|
6516
|
-
return '<div class="we-service-alert-item ' + kind + '">'
|
|
6517
|
-
+ '<span class="we-service-alert-icon">' + icon + '</span>'
|
|
6518
|
-
+ '<span class="we-service-alert-text">' + esc(providerIssue ? providerIssue.message : a.message) + actionBtn + '</span>'
|
|
6519
|
-
+ dismissBtn + '</div>';
|
|
6520
|
-
}).join('');
|
|
6590
|
+
var items = alerts.map(serviceAlertItemHtml).join('');
|
|
6521
6591
|
|
|
6522
6592
|
var html = '<div id="walle-service-alerts" class="we-service-alerts">'
|
|
6523
6593
|
+ '<div class="we-service-alerts-title">Service Alerts</div>' + items + '</div>';
|