@songsid/agend 2.3.0-beta.16 → 2.3.0-beta.18

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.
Files changed (122) hide show
  1. package/README.md +1 -1
  2. package/README.zh-TW.md +1 -1
  3. package/dist/backend/claude-code.js +13 -4
  4. package/dist/backend/claude-code.js.map +1 -1
  5. package/dist/backend/codex.js +15 -2
  6. package/dist/backend/codex.js.map +1 -1
  7. package/dist/backend-command.d.ts +2 -0
  8. package/dist/backend-command.js +3 -1
  9. package/dist/backend-command.js.map +1 -1
  10. package/dist/bug-report-cli.d.ts +30 -0
  11. package/dist/bug-report-cli.js +138 -0
  12. package/dist/bug-report-cli.js.map +1 -0
  13. package/dist/bug-report.d.ts +47 -0
  14. package/dist/bug-report.js +136 -0
  15. package/dist/bug-report.js.map +1 -0
  16. package/dist/channel/mcp-tools.js +1 -1
  17. package/dist/channel/mcp-tools.js.map +1 -1
  18. package/dist/cli.js +93 -8
  19. package/dist/cli.js.map +1 -1
  20. package/dist/config-validator.d.ts +6 -0
  21. package/dist/config-validator.js +13 -0
  22. package/dist/config-validator.js.map +1 -1
  23. package/dist/daemon.d.ts +14 -1
  24. package/dist/daemon.js +59 -3
  25. package/dist/daemon.js.map +1 -1
  26. package/dist/diagnostic-redaction.d.ts +10 -0
  27. package/dist/diagnostic-redaction.js +167 -0
  28. package/dist/diagnostic-redaction.js.map +1 -0
  29. package/dist/fleet-manager.d.ts +18 -0
  30. package/dist/fleet-manager.js +109 -8
  31. package/dist/fleet-manager.js.map +1 -1
  32. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  33. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +22 -1
  34. package/dist/instance-lifecycle.d.ts +8 -1
  35. package/dist/instance-lifecycle.js +68 -12
  36. package/dist/instance-lifecycle.js.map +1 -1
  37. package/dist/locale.js +55 -2
  38. package/dist/locale.js.map +1 -1
  39. package/dist/login-controller.d.ts +45 -0
  40. package/dist/login-controller.js +66 -3
  41. package/dist/login-controller.js.map +1 -1
  42. package/dist/ls-attach-resolve.d.ts +3 -2
  43. package/dist/ls-attach-resolve.js +10 -4
  44. package/dist/ls-attach-resolve.js.map +1 -1
  45. package/dist/ls-rows.d.ts +12 -0
  46. package/dist/ls-rows.js +82 -3
  47. package/dist/ls-rows.js.map +1 -1
  48. package/dist/needs-you.d.ts +10 -0
  49. package/dist/needs-you.js +2 -1
  50. package/dist/needs-you.js.map +1 -1
  51. package/dist/outbound-handlers.js +31 -5
  52. package/dist/outbound-handlers.js.map +1 -1
  53. package/dist/quickstart-api.d.ts +9 -0
  54. package/dist/quickstart-api.js +82 -0
  55. package/dist/quickstart-api.js.map +1 -1
  56. package/dist/service-installer.js +12 -0
  57. package/dist/service-installer.js.map +1 -1
  58. package/dist/settings-effect.js +15 -1
  59. package/dist/settings-effect.js.map +1 -1
  60. package/dist/setup-form.d.ts +1 -1
  61. package/dist/setup-form.js +23 -7
  62. package/dist/setup-form.js.map +1 -1
  63. package/dist/ui/backend-signin.js +297 -0
  64. package/dist/ui/chat-msg-actions.js +176 -0
  65. package/dist/ui/chat-store.js +33 -3
  66. package/dist/ui/chat-strings.js +8 -2
  67. package/dist/ui/chat-thread.js +44 -2
  68. package/dist/ui/panel-chat.js +37 -0
  69. package/dist/ui/panel-needs.js +4 -2
  70. package/dist/ui/panel-settings.js +6 -3
  71. package/dist/ui/shared/app-shell.js +2 -1
  72. package/dist/ui/shared/app-stream.js +2 -1
  73. package/dist/ui/shared/app.css +42 -0
  74. package/dist/ui/shared/app.js +2 -0
  75. package/dist/ui/shared/usage-dialog.js +26 -3
  76. package/dist/ui/shared/view-strings.js +10 -0
  77. package/dist/ui/web-terminal/terminal.html +1 -1
  78. package/dist/update-launch.d.ts +9 -0
  79. package/dist/update-launch.js +38 -12
  80. package/dist/update-launch.js.map +1 -1
  81. package/dist/usage/format-rich.js +6 -3
  82. package/dist/usage/format-rich.js.map +1 -1
  83. package/dist/usage/i18n-keys.d.ts +1 -1
  84. package/dist/usage/i18n-keys.js +2 -1
  85. package/dist/usage/i18n-keys.js.map +1 -1
  86. package/dist/usage/i18n.d.ts +13 -1
  87. package/dist/usage/i18n.js +29 -0
  88. package/dist/usage/i18n.js.map +1 -1
  89. package/dist/usage/providers.d.ts +43 -8
  90. package/dist/usage/providers.js +58 -20
  91. package/dist/usage/providers.js.map +1 -1
  92. package/dist/usage/usage-api.d.ts +2 -0
  93. package/dist/usage/usage-api.js +180 -55
  94. package/dist/usage/usage-api.js.map +1 -1
  95. package/dist/user-systemd.d.ts +24 -0
  96. package/dist/user-systemd.js +75 -0
  97. package/dist/user-systemd.js.map +1 -0
  98. package/dist/web-api.d.ts +10 -1
  99. package/dist/web-api.js +134 -5
  100. package/dist/web-api.js.map +1 -1
  101. package/dist/web-backend-login.d.ts +130 -0
  102. package/dist/web-backend-login.js +225 -0
  103. package/dist/web-backend-login.js.map +1 -0
  104. package/dist/web-chat-history.d.ts +31 -0
  105. package/dist/web-chat-history.js +49 -1
  106. package/dist/web-chat-history.js.map +1 -1
  107. package/dist/web-commands.d.ts +5 -1
  108. package/dist/web-commands.js +1 -1
  109. package/dist/web-commands.js.map +1 -1
  110. package/dist/web-guest-access.d.ts +3 -2
  111. package/dist/web-guest-access.js +10 -6
  112. package/dist/web-guest-access.js.map +1 -1
  113. package/dist/windows-shortcuts.d.ts +78 -0
  114. package/dist/windows-shortcuts.js +203 -0
  115. package/dist/windows-shortcuts.js.map +1 -0
  116. package/launcher/agend-install.cjs +548 -0
  117. package/launcher/install-admission.cjs +4 -39
  118. package/launcher/postinstall.cjs +14 -12
  119. package/package.json +3 -2
  120. package/scripts/preinstall-guard.cjs +2 -5
  121. package/templates/windows-shortcuts.ps1 +181 -0
  122. package/launcher/old-updater-note.cjs +0 -48
@@ -0,0 +1,10 @@
1
+ /** Public diagnostic export boundary. Never use a privacy opt-in to disable credentials. */
2
+ export declare const REDACTED = "[REDACTED]";
3
+ export interface RedactionOptions {
4
+ keepIdentifiers?: boolean;
5
+ }
6
+ export declare function redactDiagnostics(input: string, options?: RedactionOptions): string;
7
+ /** Log prose may itself be chat. Export only recognized diagnostics and stack locations. */
8
+ export declare function diagnosticLine(line: string, options?: RedactionOptions): string | null;
9
+ /** Unknown and arbitrary string fields are deliberately omitted, even inside nested objects. */
10
+ export declare function diagnosticRecord(record: Record<string, unknown>, options?: RedactionOptions): string;
@@ -0,0 +1,167 @@
1
+ import { isIP } from "node:net";
2
+ import { stripVTControlCharacters } from "node:util";
3
+ import { constants as osConstants } from "node:os";
4
+ /** Public diagnostic export boundary. Never use a privacy opt-in to disable credentials. */
5
+ export const REDACTED = '[REDACTED]';
6
+ const credentialKey = /(?:token|secret|password|passwd|credential|authorization|cookie|api[_-]?key|private[_-]?key|session|^code$|^state$)/i;
7
+ const contentKey = /(?:^msg$|^host$|^ip$|message|content|prompt|text|body|conversation|transcript|topic|chat|user|sender|recipient|instance|channel|guild|email|hostname|address|url|path|argv|args)/i;
8
+ const personalKey = /^(?:emails?|ip(?:v[46])?(?:address)?|host(?:name)?|user[_-]?name|path)$/i;
9
+ const safeCodes = /\b(?:E[A-Z_]{3,30}|SIG[A-Z]{2,10}|ERR_[A-Z_]+)\b/g;
10
+ const knownCodes = new Set([...Object.keys(osConstants.errno), ...Object.keys(osConstants.signals),
11
+ 'EAI_AGAIN', 'EAI_NONAME', 'ERR_STREAM_DESTROYED', 'ERR_STREAM_PREMATURE_CLOSE', 'ERR_UNHANDLED_ERROR',
12
+ 'ERR_HTTP_HEADERS_SENT', 'ERR_SOCKET_CLOSED', 'ERR_TLS_CERT_ALTNAME_INVALID', 'ERR_WORKER_OUT_OF_MEMORY',
13
+ 'ERR_MODULE_NOT_FOUND', 'ERR_REQUIRE_ESM', 'ERR_DLOPEN_FAILED', 'ERR_INVALID_STATE']);
14
+ const safePhrases = /Opening handshake has timed out|Connect Timeout Error|uncaught exception|unhandled rejection|gateway watchdog|MCP server died|web_terminal_created|adapter retry exhausted|out of memory|heap limit|allocation failed/gi;
15
+ /** Decode common wrappers before matching; don't preserve encoded copies of credentials. */
16
+ function decode(text) {
17
+ for (let pass = 0; pass < 4; pass++) {
18
+ const previous = text;
19
+ text = text.replace(/\\u([0-9a-f]{4})/gi, (_, n) => String.fromCharCode(parseInt(n, 16)))
20
+ .replace(/\\x([0-9a-f]{2})/gi, (_, n) => String.fromCharCode(parseInt(n, 16)))
21
+ .replace(/(?:%[0-9a-f]{2})+/gi, s => { try {
22
+ return decodeURIComponent(s);
23
+ }
24
+ catch {
25
+ return REDACTED;
26
+ } });
27
+ if (text === previous)
28
+ break;
29
+ }
30
+ // Ambiguous deeply encoded data is withheld, not partially decoded and exported.
31
+ return stripVTControlCharacters(text).replace(/(?:%[0-9a-f]{2})+/gi, REDACTED).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f\u200b-\u200f\u202a-\u202e\u2060-\u206f\ufeff]/g, '');
32
+ }
33
+ /** A credential field can contain a whole object/list or YAML block, not just a string. */
34
+ function redactContainers(text) {
35
+ const fields = /["']?[\w.-]{0,128}(?:token|secret|password|passwd|credential|authorization|cookie|session|api[_-]?key|private[_-]?key)[\w.-]{0,128}["']?\s*[:=]\s*/gi;
36
+ let cursor = 0, out = '', match;
37
+ while ((match = fields.exec(text))) {
38
+ const start = fields.lastIndex, opener = text[start];
39
+ if ((opener !== '{' && opener !== '[') || text.startsWith(REDACTED, start))
40
+ continue;
41
+ const stack = [], pairs = { '{': '}', '[': ']' };
42
+ let quote = '', escaped = false, end = text.length;
43
+ for (let i = start; i < text.length; i++) {
44
+ const c = text[i];
45
+ if (quote) {
46
+ if (escaped)
47
+ escaped = false;
48
+ else if (c === '\\')
49
+ escaped = true;
50
+ else if (c === quote)
51
+ quote = '';
52
+ continue;
53
+ }
54
+ if (c === '"' || c === "'") {
55
+ quote = c;
56
+ continue;
57
+ }
58
+ if (pairs[c])
59
+ stack.push(pairs[c]);
60
+ else if (c === '}' || c === ']') {
61
+ if (stack.pop() !== c)
62
+ break; // malformed container: withhold the rest
63
+ if (!stack.length) {
64
+ end = i + 1;
65
+ break;
66
+ }
67
+ }
68
+ }
69
+ out += text.slice(cursor, start) + REDACTED;
70
+ cursor = end;
71
+ fields.lastIndex = end;
72
+ }
73
+ text = out + text.slice(cursor);
74
+ return text.replace(/^([ \t]*["']?[\w.-]{0,128}(?:token|secret|password|passwd|credential|cookie|api[_-]?key)[\w.-]{0,128}["']?\s*:\s*)[>|][-+]?[^\r\n]*(?:\r?\n[ \t]+[^\r\n]*)*/gmi, '$1' + REDACTED);
75
+ }
76
+ export function redactDiagnostics(input, options = {}) {
77
+ // Whole structured values (including nested cookie/token objects) cannot be redacted safely token by token.
78
+ try {
79
+ const parsed = JSON.parse(input);
80
+ if (parsed && typeof parsed === 'object') {
81
+ const scrub = (value, depth) => {
82
+ if (depth > 16)
83
+ return REDACTED;
84
+ if (typeof value === 'string')
85
+ return redactDiagnostics(value, options);
86
+ if (Array.isArray(value))
87
+ return value.map(v => scrub(v, depth + 1));
88
+ if (value && typeof value === 'object')
89
+ return Object.fromEntries(Object.entries(value).map(([key, v]) => [redactDiagnostics(key, options), credentialKey.test(key) || (contentKey.test(key) && !(options.keepIdentifiers && personalKey.test(key))) ? REDACTED : scrub(v, depth + 1)]));
90
+ return value;
91
+ };
92
+ return JSON.stringify(scrub(parsed, 0));
93
+ }
94
+ }
95
+ catch { /* Free-form diagnostic/description, handled below. */ }
96
+ let text = redactContainers(decode(input));
97
+ text = text.replace(/-----BEGIN [^-\r\n]*(?:PRIVATE KEY|CERTIFICATE)-----[\s\S]*?(?:-----END [^-\r\n]+-----|$)/gi, REDACTED);
98
+ // Entire header values, including folded continuation lines; not just Bearer tokens.
99
+ text = text.replace(/\b(?:authorization|proxy-authorization|cookie|set-cookie)\s*:[^\r\n]*(?:\r?\n[ \t]+[^\r\n]*)*/gi, REDACTED);
100
+ // Quoted JSON/YAML/env/query credential values, including whitespace and multiline strings.
101
+ text = text.replace(/(["']?[\w.-]{0,128}(?:tokens?|secrets?|passwords?|passwd|credentials?[\w.-]{0,128}|authorization|cookies?|api[_-]?key|private[_-]?key|session(?:[_-]?(?:key|id))?)[\w.-]{0,128}["']?\s*[:=]\s*)(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\[REDACTED\]|[^\s,;&}\]\r\n]+)/gi, '$1' + REDACTED);
102
+ text = text.replace(/\b(?:Bearer|Basic)\s+[^\s,;"'<>]+/gi, REDACTED)
103
+ .replace(/\b(?:mfa\.[\w-]+|[\w-]{20,}\.[\w-]{6,}\.[\w-]{20,})\b/g, REDACTED)
104
+ .replace(/\b\d{5,}:[A-Za-z0-9_-]{15,}\b/g, REDACTED)
105
+ .replace(/\b(?:ya29\.[\w.-]+|1\/\/[\w.-]+|sk-[\w-]+|gh[pousr]_[\w]+|github_pat_[\w]+|xox[baprs]-[\w-]+|AKIA[A-Z0-9]{12,}|ASIA[A-Z0-9]{12,}|AIza[\w-]{20,}|npm_[\w]+|glpat-[\w-]+)\b/g, REDACTED)
106
+ .replace(/\beyJ[\w-]*\.[\w-]+\.[\w-]+\b/g, REDACTED)
107
+ .replace(/\b[a-z][a-z\d+.-]{0,127}:\/\/[^\s<>"']+/gi, REDACTED);
108
+ // Protect only recognized diagnostic locations/fields AFTER explicit credential rules.
109
+ // Do not treat scoped npm paths as emails, long module names as opaque tokens, or
110
+ // trusted line numbers/durations as chat IDs during the general privacy pass.
111
+ const kept = new Map();
112
+ const keep = (value) => { const marker = 'AGENDKEEP' + kept.size.toString(36) + 'Q'; kept.set(marker, value); return marker; };
113
+ const safePath = (value) => value.split(/[\\/]/).map(part => part.replace(/[A-Za-z0-9_+=]{24,}|\b\d{5,}\b/g, REDACTED)).join('/');
114
+ text = text.replace(/(?:…+)?(?:[a-z]:[\\/]|\/)[^\r\n<>"()]{0,512}?@songsid[\\/]agend[\\/]((?:[@\w.-]+[\\/]){0,12}[\w.-]+\.(?:[cm]?js|ts)):(\d{1,7}):(\d{1,7})/gi, (_all, relative, line, column) => keep('…/@songsid/agend/' + safePath(relative) + ':' + line + ':' + column))
115
+ .replace(/\bnode:([a-z_]+(?:\/[\w.-]+){0,12}):(\d{1,7}):(\d{1,7})/g, (_all, relative, line, column) => keep('node:' + safePath(relative) + ':' + line + ':' + column))
116
+ .replace(/"(?:duration|durationMs|attempt|exitCode|statusCode)"\s*:\s*-?\d{1,24}(?:\.\d{1,12})?(?:e[+-]?\d{1,3})?/gi, keep);
117
+ text = text
118
+ // Opaque unknown keys and unlabelled IDs. Over-redaction is intentional.
119
+ .replace(/[A-Za-z0-9_+/=-]{24,}/g, REDACTED)
120
+ .replace(/\b\d{5,}\b/g, REDACTED)
121
+ .replace(/[\"']?\b[\w.-]{0,128}(?:topic|chat|user|sender|recipient|instance|channel|guild)[_-]?id[\"']?\s*[=:]\s*(?:\[REDACTED\]|[^\s,;]+)/gi, REDACTED)
122
+ .replace(/\b(?:message|content|prompt|chat[_-]?text)\s*[=:]\s*[^\r\n]*/gi, REDACTED);
123
+ if (!options.keepIdentifiers) {
124
+ text = text.replace(/[^\s@<>"'(),;\[\]\\/:]{1,320}@[^\s@<>"'(),;\[\]\\/:]{1,320}/gu, REDACTED)
125
+ .replace(/(?:\b\d{1,3}\.){3}\d{1,3}\b/g, REDACTED)
126
+ .replace(/\[?[a-f\d:.]{0,64}:[a-f\d:.]{1,64}(?:%[\w.-]{1,64})?\]?/gi, value => isIP(value.replace(/^\[|\]$/g, '').split('%')[0]) === 6 ? REDACTED : value)
127
+ .replace(/(?<![\p{L}\p{N}_-])[\p{L}\p{N}-][\p{L}\p{N}.-]{0,252}\.[\p{L}][\p{L}\p{N}-]{0,62}(?![\p{L}\p{N}_-])/gu, value => /\.(?:ya?ml|json|log|[cm]?js|tsx?|md|txt|sh|ps1)$/i.test(value) || /^(?:e\.g|i\.e)$/i.test(value) ? value : REDACTED)
128
+ .replace(/(\/(?:home|Users|mnt\/[a-z]\/Users)\/)[^\/\s]+/gi, '$1' + REDACTED)
129
+ .replace(/([a-z]:\\Users\\)[^\\\s]+/gi, '$1' + REDACTED)
130
+ .replace(/[\"']?\b(?:host(?:name)?|user(?:name)?)[\"']?\s*[=:]\s*(?:\[REDACTED\]|[^\s,;]+)/gi, REDACTED);
131
+ }
132
+ return text.replace(/AGENDKEEP[0-9a-z]+Q/g, value => kept.get(value) ?? value);
133
+ }
134
+ /** Log prose may itself be chat. Export only recognized diagnostics and stack locations. */
135
+ export function diagnosticLine(line, options = {}) {
136
+ const codes = [...new Set((line.match(safeCodes) ?? []).filter(code => knownCodes.has(code)))];
137
+ const phrases = [...new Set(line.match(safePhrases) ?? [])];
138
+ const stack = line.match(/^\s*at\s+(?:[\w.$<>]+\s+)?\(?((?:\/|[a-z]:\\|node:)[^\r\n]*?:\d+:\d+)\)?\s*$/i);
139
+ if (stack)
140
+ return redactDiagnostics('at ' + stack[1], options);
141
+ const diagnostic = [...phrases, ...codes].join('; ');
142
+ return diagnostic ? redactDiagnostics(diagnostic, options) : null;
143
+ }
144
+ /** Unknown and arbitrary string fields are deliberately omitted, even inside nested objects. */
145
+ export function diagnosticRecord(record, options = {}) {
146
+ const out = {};
147
+ for (const [key, value] of Object.entries(record)) {
148
+ if (credentialKey.test(key) || contentKey.test(key))
149
+ continue;
150
+ if (/^(?:level|duration|durationMs|attempt|exitCode|statusCode)$/.test(key) && typeof value === 'number')
151
+ out[key] = value;
152
+ if (/^(?:backend|runtime)$/.test(key) && typeof value === 'string' && /^(?:claude-code|codex|kiro-cli|opencode|grok|muse|antigravity|node)$/.test(value))
153
+ out[key] = value;
154
+ if (/^(?:version|cliVersion|backendVersion)$/.test(key) && typeof value === 'string' && /^\d{1,3}\.\d{1,3}\.\d{1,3}(?:-[a-z]+\.\d{1,3})?$/.test(value))
155
+ out[key] = value;
156
+ if (key === 'err' || key === 'error') {
157
+ const error = value && typeof value === 'object' ? value : { message: value };
158
+ const lines = Object.values(error).filter((s) => typeof s === 'string').flatMap(s => s.split('\n'));
159
+ out.error = lines.map(l => diagnosticLine(l, options)).filter(Boolean).join('\n') || '[Free-form error details withheld]';
160
+ }
161
+ }
162
+ // msg is inspected for canonical diagnostics, never exported as raw prose.
163
+ if (typeof record.msg === 'string')
164
+ out.diagnostic = diagnosticLine(record.msg, options) ?? '[Free-form log text withheld]';
165
+ return JSON.stringify(out);
166
+ }
167
+ //# sourceMappingURL=diagnostic-redaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostic-redaction.js","sourceRoot":"","sources":["../src/diagnostic-redaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,4FAA4F;AAC5F,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAC;AAGrC,MAAM,aAAa,GAAG,sHAAsH,CAAC;AAC7I,MAAM,UAAU,GAAG,mLAAmL,CAAC;AACvM,MAAM,WAAW,GAAG,0EAA0E,CAAC;AAC/F,MAAM,SAAS,GAAG,mDAAmD,CAAC;AACtE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;IAChG,WAAW,EAAE,YAAY,EAAE,sBAAsB,EAAE,4BAA4B,EAAE,qBAAqB;IACtG,uBAAuB,EAAE,mBAAmB,EAAE,8BAA8B,EAAE,0BAA0B;IACxG,sBAAsB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,mBAAmB,CAAC,CAAC,CAAC;AACxF,MAAM,WAAW,GAAG,yNAAyN,CAAC;AAE9O,4FAA4F;AAC5F,SAAS,MAAM,CAAC,IAAY;IAC1B,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC;QACtB,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;aAC9F,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAAE,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;aACrF,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;YAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,QAAQ,CAAC;QAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7G,IAAI,IAAI,KAAK,QAAQ;YAAE,MAAM;IAC/B,CAAC;IACD,iFAAiF;IACjF,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,8FAA8F,EAAE,EAAE,CAAC,CAAC;AAC7L,CAAC;AAED,2FAA2F;AAC3F,SAAS,gBAAgB,CAAC,IAAY;IACpC,MAAM,MAAM,GAAG,sJAAsJ,CAAC;IACtK,IAAI,MAAM,GAAG,CAAC,EAAE,GAAG,GAAG,EAAE,EAAE,KAA6B,CAAC;IACxD,OAAO,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC;YAAE,SAAS;QACrF,MAAM,KAAK,GAAa,EAAE,EAAE,KAAK,GAA2B,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACnF,IAAI,KAAK,GAAG,EAAE,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC;QACnD,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,IAAI,KAAK,EAAE,CAAC;gBAAC,IAAI,OAAO;oBAAE,OAAO,GAAG,KAAK,CAAC;qBAAM,IAAI,CAAC,KAAK,IAAI;oBAAE,OAAO,GAAG,IAAI,CAAC;qBAAM,IAAI,CAAC,KAAK,KAAK;oBAAE,KAAK,GAAG,EAAE,CAAC;gBAAC,SAAS;YAAC,CAAC;YAC7H,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBAAC,KAAK,GAAG,CAAC,CAAC;gBAAC,SAAS;YAAC,CAAC;YACpD,IAAI,KAAK,CAAC,CAAC,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;iBAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBAChC,IAAI,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC;oBAAE,MAAM,CAAC,yCAAyC;gBACvE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;oBAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;oBAAC,MAAM;gBAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QACD,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC;QAC5C,MAAM,GAAG,GAAG,CAAC;QAAC,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC;IACvC,CAAC;IACD,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC,OAAO,CAAC,gKAAgK,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC;AACzM,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,UAA4B,EAAE;IAC7E,4GAA4G;IAC5G,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,CAAC,KAAc,EAAE,KAAa,EAAW,EAAE;gBACvD,IAAI,KAAK,GAAG,EAAE;oBAAE,OAAO,QAAQ,CAAC;gBAChC,IAAI,OAAO,KAAK,KAAK,QAAQ;oBAAE,OAAO,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBACxE,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;oBAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;gBACrE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;oBAAE,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,eAAe,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACzR,OAAO,KAAK,CAAC;YACf,CAAC,CAAC;YACF,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,sDAAsD,CAAC,CAAC;IAClE,IAAI,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,6FAA6F,EAAE,QAAQ,CAAC,CAAC;IAC7H,qFAAqF;IACrF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iGAAiG,EAAE,QAAQ,CAAC,CAAC;IACjI,4FAA4F;IAC5F,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,wQAAwQ,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC;IAC/S,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,qCAAqC,EAAE,QAAQ,CAAC;SACjE,OAAO,CAAC,wDAAwD,EAAE,QAAQ,CAAC;SAC3E,OAAO,CAAC,gCAAgC,EAAE,QAAQ,CAAC;SACnD,OAAO,CAAC,4KAA4K,EAAE,QAAQ,CAAC;SAC/L,OAAO,CAAC,gCAAgC,EAAE,QAAQ,CAAC;SACnD,OAAO,CAAC,2CAA2C,EAAE,QAAQ,CAAC,CAAC;IAClE,uFAAuF;IACvF,kFAAkF;IAClF,8EAA8E;IAC9E,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,GAAG,MAAM,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IAC/I,MAAM,QAAQ,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,iCAAiC,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClJ,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,4IAA4I,EAC9J,CAAC,IAAI,EAAE,QAAgB,EAAE,IAAY,EAAE,MAAc,EAAE,EAAE,CAAC,IAAI,CAAC,mBAAmB,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC;SACpI,OAAO,CAAC,0DAA0D,EACjE,CAAC,IAAI,EAAE,QAAgB,EAAE,IAAY,EAAE,MAAc,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC;SAC1H,OAAO,CAAC,2GAA2G,EAAE,IAAI,CAAC,CAAC;IAC9H,IAAI,GAAG,IAAI;QACT,yEAAyE;SACxE,OAAO,CAAC,wBAAwB,EAAE,QAAQ,CAAC;SAC3C,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC;SAChC,OAAO,CAAC,oIAAoI,EAAE,QAAQ,CAAC;SACvJ,OAAO,CAAC,gEAAgE,EAAE,QAAQ,CAAC,CAAC;IACvF,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;QAC7B,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,+DAA+D,EAAE,QAAQ,CAAC;aAC3F,OAAO,CAAC,8BAA8B,EAAE,QAAQ,CAAC;aACjD,OAAO,CAAC,2DAA2D,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;aACzJ,OAAO,CAAC,uGAAuG,EAAE,KAAK,CAAC,EAAE,CAAC,mDAAmD,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;aAC/O,OAAO,CAAC,kDAAkD,EAAE,IAAI,GAAG,QAAQ,CAAC;aAC5E,OAAO,CAAC,6BAA6B,EAAE,IAAI,GAAG,QAAQ,CAAC;aACvD,OAAO,CAAC,oFAAoF,EAAE,QAAQ,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;AACjF,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,UAA4B,EAAE;IACzE,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/F,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,+EAA+E,CAAC,CAAC;IAC1G,IAAI,KAAK;QAAE,OAAO,iBAAiB,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/D,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,OAAO,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpE,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,gBAAgB,CAAC,MAA+B,EAAE,UAA4B,EAAE;IAC9F,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QAC9D,IAAI,6DAA6D,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3H,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,sEAAsE,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3K,IAAI,yCAAyC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,kDAAkD,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACzK,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAgC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;YACzG,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACjH,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,oCAAoC,CAAC;QAC5H,CAAC;IACH,CAAC;IACD,2EAA2E;IAC3E,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ;QAAE,GAAG,CAAC,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,+BAA+B,CAAC;IAC5H,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -76,6 +76,7 @@ import { AgentMods } from "./web-mod-agent.js";
76
76
  import { type CachedImage } from "./web-images.js";
77
77
  import { WebSettingsTools } from "./web-settings-tools.js";
78
78
  import { type Delegation, type TrafficItem } from "./fleet-traffic.js";
79
+ import { WebBackendLogins, type SignInTarget } from "./web-backend-login.js";
79
80
  import { type RawConfigPatch } from "./settings-api.js";
80
81
  import { type AgentEndpointContext } from "./agent-endpoint.js";
81
82
  import { ClassicChannelManager } from "./classic-channel-manager.js";
@@ -3057,6 +3058,23 @@ export declare class FleetManager implements FleetContext, LifecycleContext, Arc
3057
3058
  */
3058
3059
  private readonly loginWindow;
3059
3060
  private get webLogin();
3061
+ /** #2233: sign-ins started from the web dashboard (local admin sessions), on the same controller as chat's. */
3062
+ private webBackendLoginsInstance;
3063
+ get webBackendLogins(): WebBackendLogins;
3064
+ /** #2233: the login a paused-for-sign-in instance needs, when the dashboard can sign it in. */
3065
+ private signInFor;
3066
+ /**
3067
+ * #2233: Settings → Agents' sign-in list — every backend's default login as the availability snapshot has it, then
3068
+ * each credential profile an agent uses (its own login state, read offline).
3069
+ */
3070
+ backendSignInTargets(): SignInTarget[];
3071
+ /** #2233: the session with this handle is live, admin, local and of the current dashboard token. */
3072
+ private isLocalAdminSession;
3073
+ /**
3074
+ * #2233: `/login` for a dashboard requester. The same controller and checks; a CLI that is not installed is refused
3075
+ * with its install command (chat's install-first runs a terminal of its own, which the dashboard does not offer).
3076
+ */
3077
+ private startWebDashboardLogin;
3060
3078
  /**
3061
3079
  * Post the backend chooser for a bare `/login`. Caller enforces admin.
3062
3080
  * Returns why the chooser could not be posted, for the caller to tell the
@@ -190,6 +190,7 @@ import { installedChannel, updateNoticeKey } from "./update-check.js";
190
190
  import { resolveInstalledAgend } from "./update-dispatch.js";
191
191
  import { resolveUpdateLaunch, openUpdateLog, updateLogPath, watchUpdateLaunch } from "./update-launch.js";
192
192
  import { admitProfileLogin, LoginController, LOGIN_TOKEN_RESEND_PREFIX, POST_LOGIN_RECOVERY_DEADLINE_MS } from "./login-controller.js";
193
+ import { WebBackendLogins, dashboardSignInSupported } from "./web-backend-login.js";
193
194
  import { runBeforeDeadline } from "./deadline.js";
194
195
  import { LoginWindowLock } from "./login-window-lock.js";
195
196
  import { handleSettingsRequest } from "./settings-api.js";
@@ -248,7 +249,7 @@ import { PROVIDER_SECRET_SPECS, providerSecretSpec, providerRegistryEnvKeys, isR
248
249
  /** A self-restart is a whole service restart; 120s is the apply budget, not this. */
249
250
  const SELF_RESTART_DEADLINE_MS = 300_000;
250
251
  import { APPLY_FLEET_TARGET, ApplyJobStore, viewOf, } from "./apply-job.js";
251
- import { CHAT_PROFILE_LOGIN_BACKENDS, credentialHomeSpec, credentialProfileHome, credentialProfileLogin, credentialProfileStoreHome, credentialSwitchCarriesConversation, credentialSwitchStartsFresh, instanceCredentialProfile } from "./backend/credential-profile.js";
252
+ import { CHAT_PROFILE_LOGIN_BACKENDS, credentialHomeSpec, credentialProfileHome, credentialProfileLogin, credentialProfileLoginCommand, credentialProfileStoreHome, credentialSwitchCarriesConversation, credentialSwitchStartsFresh, instanceCredentialProfile } from "./backend/credential-profile.js";
252
253
  import { carryClaudeConversation } from "./backend/claude-carry.js";
253
254
  import { mergeBackendOptions, profileKey, profileName, profileOf } from "./classic-bindings.js";
254
255
  import { classifyInstanceChange, CLASSIC_HOT_CONFIG_KEYS, HOT_INSTANCE_CONFIG_KEYS, hotConfigUpdate, } from "./instance-config-impact.js";
@@ -261,6 +262,14 @@ import { SpawnGate } from "./spawn-gate.js";
261
262
  import { BackendOutageTracker } from "./backend-outage.js";
262
263
  import { canUnlockAdvancedTips, DailyTipScheduler, selectTip, visibleTipLevels, } from "./tips.js";
263
264
  import { getTmuxSession } from "./config.js";
265
+ // Closed migration cohort: these 11 names existed immediately before the first
266
+ // managed-skill publisher (a1ac499f; parent e267e684). Never grow this set when
267
+ // adding a bundled skill: its new name is not proof of old AgEnD ownership.
268
+ const PRE_MANIFEST_KIRO_SKILLS = new Set([
269
+ "backend-providers", "cross-instance-messaging", "fleet-config", "fleet-health",
270
+ "fleet-restart", "instance-lifecycle", "model-discovery", "multi-channel",
271
+ "scheduling", "session-management", "tui-effort",
272
+ ]);
264
273
  export function resolveReplyThreadId(argsThreadId, instanceConfig) {
265
274
  if (typeof argsThreadId === "string" && argsThreadId.length > 0) {
266
275
  return argsThreadId;
@@ -7384,6 +7393,10 @@ export class FleetManager {
7384
7393
  // operation so an older stop never reports. No pending operation means
7385
7394
  // a plain boot — nothing was stopped by an operation, report nothing.
7386
7395
  const backgroundWorkEndedNames = this.collectBackgroundWorkEndedNames(configuredNames, pendingUpdateProgress?.startedAt ?? null);
7396
+ // #2264: an update whose fleet comes back on another version did not land — say so in its final message (the
7397
+ // version it aimed for, the one running, and the one repair), never just "updated".
7398
+ const aimedAt = pendingProgressOperation === "update" ? pendingUpdateProgress?.progress.version : undefined;
7399
+ const notLanded = aimedAt && agendVersion !== "unknown" && aimedAt !== agendVersion ? t("update.not_landed", aimedAt, agendVersion) : null;
7387
7400
  const completionSummary = {
7388
7401
  running: started,
7389
7402
  total,
@@ -7393,7 +7406,7 @@ export class FleetManager {
7393
7406
  backgroundWorkEndedNames,
7394
7407
  ...(cleanupOutcome && "render" in cleanupOutcome
7395
7408
  ? { warningText: cleanupOutcome.render.warning, warningCommand: cleanupOutcome.render.cleanupCommand }
7396
- : {}),
7409
+ : notLanded ? { warningText: notLanded } : {}),
7397
7410
  };
7398
7411
  const progressCompleted = await startupProgress.finish(completionSummary);
7399
7412
  // Armed only after the completion posted: an append racing ahead of it would be overwritten by it.
@@ -13469,6 +13482,7 @@ export class FleetManager {
13469
13482
  interactionSummary: p.interaction_summary ?? null,
13470
13483
  pauseReason, pausedAt,
13471
13484
  crashedAt: crashed ? (this.needsCrashedAt.get(name) ?? null) : null,
13485
+ signIn: pauseReason === "auth" ? this.signInFor(name) : null,
13472
13486
  });
13473
13487
  }
13474
13488
  return out;
@@ -14179,7 +14193,7 @@ export class FleetManager {
14179
14193
  const current = this.resolveInstanceEffort(instance).effort;
14180
14194
  return { current, options: levels.map(l => ({ id: l, label: this.effortChoiceLabel(l, current) })) };
14181
14195
  },
14182
- backendStep: async (instance, args) => {
14196
+ backendStep: async (instance, args, stepOpts) => {
14183
14197
  const refusal = this.backendCommandRefusal(instance);
14184
14198
  if (refusal)
14185
14199
  return { error: refusal };
@@ -14189,7 +14203,13 @@ export class FleetManager {
14189
14203
  return step.noop ? { text: step.text } : { error: step.text };
14190
14204
  if (step.kind === "choose") {
14191
14205
  const id = (token) => joinBackendArgs([...step.args, token]);
14192
- return { choices: { prompt: step.text, current: step.current === null ? null : id(step.current), options: step.choices.map(c => ({ id: id(c.token), label: c.label, ...(c.disabled ? { disabled: c.disabled } : {}) })) } };
14206
+ return { choices: { prompt: step.text, current: step.current === null ? null : id(step.current), options: step.choices.map(c => {
14207
+ // #2233: the web offers Sign in beside it, so its reason is no longer "use /login".
14208
+ // Not over the public link: Sign in is refused there, so the choice keeps its "(use /login)" (#2262 review).
14209
+ const signIn = c.signIn && !stepOpts?.publicLink && dashboardSignInSupported(c.signIn) ? c.signIn : null;
14210
+ const disabled = signIn ? t("backend.reason_not_signed_in_web") : c.disabled;
14211
+ return { id: id(c.token), label: signIn ? `${c.token} — ${disabled}` : c.label, ...(disabled ? { disabled } : {}), ...(signIn ? { signIn } : {}) };
14212
+ }) } };
14193
14213
  }
14194
14214
  const now = performance.now();
14195
14215
  for (const [k, v] of this.webBackendTokens)
@@ -15353,6 +15373,86 @@ export class FleetManager {
15353
15373
  }
15354
15374
  return this.loginController;
15355
15375
  }
15376
+ /** #2233: sign-ins started from the web dashboard (local admin sessions), on the same controller as chat's. */
15377
+ webBackendLoginsInstance = null;
15378
+ get webBackendLogins() {
15379
+ this.webBackendLoginsInstance ??= new WebBackendLogins({
15380
+ isLocalAdmin: handle => this.isLocalAdminSession(handle),
15381
+ start: (backend, chat, opts) => this.startWebDashboardLogin(backend, chat, opts),
15382
+ accessToken: sink => this.loginController?.webAccessToken(sink) ?? null,
15383
+ isActiveFor: sink => this.loginController?.isActiveFor(sink) ?? false,
15384
+ cancelFor: sink => this.loginController?.cancelFor(sink) ?? Promise.resolve(false),
15385
+ cancel: () => this.cancelLoginSession(),
15386
+ notice: ({ shown, label, handle }) => this.notifyFleetError(t("login.web_dashboard_notice", shown, label, handle.slice(0, 8))),
15387
+ // No details in the event: each page reads its own view (GET /ui/backend-login), as its session may see it.
15388
+ changed: () => broadcastSseEvent(this.sseClients, "backend_login", {}, err => this.logger.debug({ err }, "backend_login event failed")),
15389
+ });
15390
+ return this.webBackendLoginsInstance;
15391
+ }
15392
+ /** #2233: the login a paused-for-sign-in instance needs, when the dashboard can sign it in. */
15393
+ signInFor(name) {
15394
+ const backend = this.backendNameForInstance(name);
15395
+ const profile = this.credentialProfileOf(name, backend);
15396
+ return dashboardSignInSupported(backend, profile) ? { backend, ...(profile ? { profile } : {}) } : null;
15397
+ }
15398
+ /**
15399
+ * #2233: Settings → Agents' sign-in list — every backend's default login as the availability snapshot has it, then
15400
+ * each credential profile an agent uses (its own login state, read offline).
15401
+ */
15402
+ backendSignInTargets() {
15403
+ const out = [];
15404
+ for (const row of this.backendAvailabilityRows()) {
15405
+ const flow = LOGIN_FLOWS[row.backend];
15406
+ const install = BACKEND_INSTALLATION_INFO[row.backend]?.install;
15407
+ out.push({
15408
+ // Not installed or too old: signing in would not help — the row says how to install (or update) it instead.
15409
+ backend: row.backend, state: row.state, signIn: row.state !== "not_installed" && row.state !== "too_old" && dashboardSignInSupported(row.backend),
15410
+ ...(flow?.remoteLogin === "unsupported" ? { hostCommand: flow.command } : {}),
15411
+ ...(row.state === "not_installed" && install ? { install } : {}),
15412
+ ...(row.state === "too_old" && install ? { update: install } : {}),
15413
+ });
15414
+ }
15415
+ const seen = new Set();
15416
+ for (const name of Object.keys(this.fleetConfig?.instances ?? {}).sort()) {
15417
+ const backend = this.backendNameForInstance(name);
15418
+ const profile = this.credentialProfileOf(name, backend);
15419
+ if (!profile || seen.has(`${backend}\0${profile}`))
15420
+ continue;
15421
+ seen.add(`${backend}\0${profile}`);
15422
+ let login = "unknown";
15423
+ try {
15424
+ login = credentialProfileLogin(this.dataDir, backend, profile).state;
15425
+ }
15426
+ catch { /* unknown */ }
15427
+ const signIn = dashboardSignInSupported(backend, profile);
15428
+ const hostCommand = signIn ? null : credentialProfileLoginCommand(this.dataDir, backend, profile);
15429
+ out.push({ backend, profile, state: login === "logged-in" ? "usable" : login === "signed-out" ? "not_signed_in" : "unknown", signIn,
15430
+ ...(hostCommand ? { hostCommand } : {}) });
15431
+ }
15432
+ return out;
15433
+ }
15434
+ /** #2233: the session with this handle is live, admin, local and of the current dashboard token. */
15435
+ isLocalAdminSession(handle) {
15436
+ const sessions = this.webSessions, token = this.webToken;
15437
+ const record = sessions?.findByHandle(handle);
15438
+ return !!record && !!token && record.tier === "admin" && record.surface === "local" && sessions.isCurrent(record, tokenEpoch(token));
15439
+ }
15440
+ /**
15441
+ * #2233: `/login` for a dashboard requester. The same controller and checks; a CLI that is not installed is refused
15442
+ * with its install command (chat's install-first runs a terminal of its own, which the dashboard does not offer).
15443
+ */
15444
+ startWebDashboardLogin(backendArg, chat, opts) {
15445
+ const wanted = LOGIN_BACKEND_ALIASES[backendArg.toLowerCase()] ?? backendArg.toLowerCase();
15446
+ if (isRemovedBackend(wanted))
15447
+ return Promise.resolve(removedBackendMessage(wanted));
15448
+ const info = BACKEND_INSTALLATION_INFO[wanted];
15449
+ if (info && LOGIN_FLOWS[wanted]?.remoteLogin !== "unsupported" && !this.isCliInstalled(wanted)) {
15450
+ return Promise.resolve(t("login.web_dashboard_not_installed", wanted, info.install));
15451
+ }
15452
+ if (!opts.skipAuthCheck)
15453
+ this.recordLoginFlow(wanted, "login", chat.userId);
15454
+ return this.webLogin.start(backendArg, chat, opts);
15455
+ }
15356
15456
  /**
15357
15457
  * Post the backend chooser for a bare `/login`. Caller enforces admin.
15358
15458
  * Returns why the chooser could not be posted, for the caller to tell the
@@ -16089,6 +16189,7 @@ export class FleetManager {
16089
16189
  links: m.links,
16090
16190
  channel: m.channel,
16091
16191
  from: m.from,
16192
+ replyTo: m.replyTo,
16092
16193
  });
16093
16194
  broadcastSseEvent(this.sseClients, event, recorded, onError, this.webChatHistory.cursorOf(recorded));
16094
16195
  // #1629: live-only receipt after the recorded agent reply. Never persist/replay it or give it an SSE cursor.
@@ -16435,16 +16536,16 @@ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, sessi
16435
16536
  .filter(name => existsSync(join(srcSkills, name, "SKILL.md")))
16436
16537
  .sort();
16437
16538
  // Pre-manifest Kiro General copied bundled skills directly into
16438
- // .kiro/skills. If every existing skill is still a bundled name, this is
16439
- // the unambiguous legacy layout: adopt it once, update it below, and write
16440
- // the ownership manifest. Any extra skill name keeps the directory fully
16539
+ // .kiro/skills. If every existing skill is still bundled AND belongs to
16540
+ // the closed pre-manifest cohort, adopt it once, update it below, and
16541
+ // write the ownership manifest. Any extra skill name keeps the directory fully
16441
16542
  // on the user-owned/collision path.
16442
16543
  if (!hadManifest && adoptLegacyUnmanaged) {
16443
16544
  const existing = readdirSync(destSkills, { withFileTypes: true })
16444
16545
  .filter(entry => entry.isDirectory() && existsSync(join(destSkills, entry.name, "SKILL.md")))
16445
16546
  .map(entry => entry.name)
16446
16547
  .sort();
16447
- if (existing.length > 0 && existing.every(name => bundled.includes(name))) {
16548
+ if (existing.length > 0 && existing.every(name => bundled.includes(name) && PRE_MANIFEST_KIRO_SKILLS.has(name))) {
16448
16549
  previouslyManaged = existing;
16449
16550
  this.logger.info({ skills: existing, destSkills }, "Adopted legacy AgEnD skills into managed ownership");
16450
16551
  }