maka-agent 0.2.1-dev.84.20261014 → 0.2.1-dev.86.20261014

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 (88) hide show
  1. package/dist/acp/session-configuration.js +1 -0
  2. package/dist/run-command-core.js +4 -7
  3. package/dist/runtime-host-session-driver.js +4 -4
  4. package/dist/tui-copy-catalog.js +3 -0
  5. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  6. package/node_modules/@maka/antigravity-acp-plugin/dist/plugin.mjs +2 -1
  7. package/node_modules/@maka/core/dist/image-delivery.js +83 -0
  8. package/node_modules/@maka/core/dist/image-markdown.js +44 -0
  9. package/node_modules/@maka/core/dist/model-metadata.js +8 -0
  10. package/node_modules/@maka/core/dist/model-thinking.js +10 -29
  11. package/node_modules/@maka/core/dist/recall.js +10 -2
  12. package/node_modules/@maka/core/dist/session.js +2 -0
  13. package/node_modules/@maka/core/dist/settings.js +2 -0
  14. package/node_modules/@maka/core/dist/subagent-settings.js +2 -1
  15. package/node_modules/@maka/core/package.json +4 -1
  16. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +23 -0
  17. package/node_modules/@maka/runtime/dist/filesystem-executor.js +2 -0
  18. package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +7 -3
  19. package/node_modules/@maka/runtime/dist/filesystem-worker/protocol.js +6 -4
  20. package/node_modules/@maka/runtime/dist/image-file-reader.js +83 -0
  21. package/node_modules/@maka/runtime/dist/image-file.js +43 -10
  22. package/node_modules/@maka/runtime/dist/model-adapter.js +3 -0
  23. package/node_modules/@maka/runtime/dist/model-factory.js +38 -98
  24. package/node_modules/@maka/runtime/dist/network/public-network-policy.js +97 -0
  25. package/node_modules/@maka/runtime/dist/network/scoped-fetch-transport.js +57 -4
  26. package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +135 -0
  27. package/node_modules/@maka/runtime/dist/plugin-source-service.js +123 -0
  28. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +3 -0
  29. package/node_modules/@maka/runtime/dist/plugin-turn-finish-service.js +63 -0
  30. package/node_modules/@maka/runtime/dist/system-prompt/main-session-prompt.js +3 -0
  31. package/node_modules/@maka/runtime/dist/tool-availability.js +3 -1
  32. package/node_modules/@maka/runtime/dist/tool-free-model-call.js +21 -3
  33. package/node_modules/@maka/runtime/dist/workers/filesystem-worker.mjs +69 -12
  34. package/node_modules/@maka/runtime/dist/workspace-executor.js +3 -3
  35. package/node_modules/@maka/runtime/package.json +4 -0
  36. package/node_modules/@maka/runtime-host/dist/assistant/background-sessions.js +32 -0
  37. package/node_modules/@maka/runtime-host/dist/assistant/execution-outcome.js +52 -0
  38. package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/assistant.js +236 -0
  39. package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/controller.js +192 -0
  40. package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/host.js +71 -0
  41. package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/prompt.js +26 -0
  42. package/node_modules/@maka/runtime-host/dist/assistant/index-initiative/store.js +228 -0
  43. package/node_modules/@maka/runtime-host/dist/assistant/memory-network/controller.js +1121 -0
  44. package/node_modules/@maka/runtime-host/dist/assistant/memory-network/corpus.js +431 -0
  45. package/node_modules/@maka/runtime-host/dist/assistant/memory-network/extractor.js +143 -0
  46. package/node_modules/@maka/runtime-host/dist/assistant/memory-network/host.js +402 -0
  47. package/node_modules/@maka/runtime-host/dist/assistant/memory-network/store.js +388 -0
  48. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/controller.js +316 -0
  49. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/delegation.js +232 -0
  50. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/delivery.js +250 -0
  51. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/files.js +103 -0
  52. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/host-types.js +19 -0
  53. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/host.js +135 -0
  54. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/matter.js +22 -0
  55. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/prompt.js +51 -0
  56. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/review.js +132 -0
  57. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/store.js +885 -0
  58. package/node_modules/@maka/runtime-host/dist/assistant/proactive-matters/tools.js +176 -0
  59. package/node_modules/@maka/runtime-host/dist/assistant/service.js +421 -0
  60. package/node_modules/@maka/runtime-host/dist/protocol/artifact.js +2 -0
  61. package/node_modules/@maka/runtime-host/dist/protocol/image-delivery.js +70 -0
  62. package/node_modules/@maka/runtime-host/dist/protocol/index.js +7 -1
  63. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +4 -1
  64. package/node_modules/@maka/runtime-host/dist/protocol/personal-assistant.js +58 -0
  65. package/node_modules/@maka/runtime-host/dist/server/artifact-coordinator.js +10 -1
  66. package/node_modules/@maka/runtime-host/dist/server/chat-image-artifact.js +41 -0
  67. package/node_modules/@maka/runtime-host/dist/server/chat-image-delivery.js +232 -0
  68. package/node_modules/@maka/runtime-host/dist/server/chat-image-markdown.js +26 -0
  69. package/node_modules/@maka/runtime-host/dist/server/chat-image-source.js +121 -0
  70. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +257 -14
  71. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +6 -0
  72. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +1 -0
  73. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +12 -2
  74. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
  75. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +40 -13
  76. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +3 -0
  77. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +40 -0
  78. package/node_modules/@maka/storage/dist/artifact-image-storage.js +62 -0
  79. package/node_modules/@maka/storage/dist/artifact-metadata-codec.js +3 -0
  80. package/node_modules/@maka/storage/dist/artifact-store.js +67 -5
  81. package/node_modules/@maka/storage/dist/artifact-stores.js +14 -0
  82. package/node_modules/@maka/storage/dist/execution-stores.js +2 -0
  83. package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
  84. package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +44 -0
  85. package/node_modules/@maka/storage/dist/sqlite-artifact-schema.js +8 -1
  86. package/node_modules/@maka/storage/dist/sqlite-image-delivery.js +63 -0
  87. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +9 -0
  88. package/package.json +1 -1
@@ -28,6 +28,7 @@ const PERMISSION_NAMES = {
28
28
  const THINKING_NAMES = {
29
29
  default: 'Default',
30
30
  off: 'Off',
31
+ on: 'On',
31
32
  minimal: 'Minimal',
32
33
  low: 'Low',
33
34
  medium: 'Medium',
@@ -117,14 +117,13 @@ export function parseMakaRunArgs(argv) {
117
117
  ...(flags.get('cwd') !== undefined ? { cwd: flags.get('cwd') } : {}),
118
118
  ...(flags.get('connection') !== undefined ? { connection: flags.get('connection') } : {}),
119
119
  ...(flags.get('model') !== undefined ? { model: flags.get('model') } : {}),
120
- ...(thinking !== undefined && thinking !== 'default' ? { thinking } : {}),
120
+ ...(thinking !== undefined ? { thinking: thinking === 'default' ? null : thinking } : {}),
121
121
  ...(timeoutSeconds !== undefined ? { timeoutMs: Math.ceil(timeoutSeconds * 1_000) } : {}),
122
122
  ...(parsedMaxSteps !== undefined ? { maxSteps: parsedMaxSteps } : {}),
123
123
  ...(booleanFlags.has('yolo') ? { yolo: true } : {}),
124
124
  ...(resumeId !== undefined ? { resumeId } : {}),
125
125
  ...(continueLatest ? { continueLatest: true } : {}),
126
126
  ...(graph ? { graph: true } : {}),
127
- ...(thinking === 'default' ? { thinkingDefaultExplicit: true } : {}),
128
127
  ...(flags.get('host') !== undefined ? { hostProfileId: flags.get('host') } : {}),
129
128
  ...(flags.get('project') !== undefined ? { projectId: flags.get('project') } : {}),
130
129
  },
@@ -159,10 +158,8 @@ export async function runMakaTextCliCore(argv, adapter, overrides = {}) {
159
158
  ? { explicitConnection: parsed.options.connection }
160
159
  : {}),
161
160
  ...(parsed.options.model !== undefined ? { explicitModel: parsed.options.model } : {}),
162
- thinkingSpecified: parsed.options.thinking !== undefined || parsed.options.thinkingDefaultExplicit === true,
163
- ...(parsed.options.thinking !== undefined
164
- ? { explicitThinking: parsed.options.thinking }
165
- : {}),
161
+ thinkingSpecified: parsed.options.thinking !== undefined,
162
+ ...(parsed.options.thinking != null ? { explicitThinking: parsed.options.thinking } : {}),
166
163
  }, {
167
164
  canonicalizeDirectory: canonicalDirectory,
168
165
  ...(parsed.options.hostProfileId && parsed.options.hostProfileId !== 'local'
@@ -381,7 +378,7 @@ function makaRunHelpText(cliCommand) {
381
378
  ' --host <profile-id> Connect through a saved Runtime Host profile',
382
379
  ' --project <project-id> Select an existing Project on a remote Host',
383
380
  ' --model <id> Model to use',
384
- ' --thinking <level> off|minimal|low|medium|high|xhigh|max|default',
381
+ ' --thinking <level> off|on|minimal|low|medium|high|xhigh|max|default',
385
382
  ' --timeout <seconds> Invocation timeout',
386
383
  ' --max-steps <count> Tool-step cap',
387
384
  ' --yolo Give this task full access to your files and network',
@@ -159,12 +159,12 @@ class RuntimeHostMakaSessionDriverImpl {
159
159
  this.#llmConnectionId = input.llmConnectionId;
160
160
  this.#llmConnectionSlug = input.llmConnectionSlug;
161
161
  this.#model = input.model;
162
- this.#thinkingLevel = input.thinkingLevel;
162
+ this.#thinkingLevel = input.thinkingLevel ?? undefined;
163
163
  // An omitted mode stays omitted: the Host applies its configured default.
164
164
  // Substituting a literal `ask` here would make the CLI a second authority
165
165
  // over the starting boundary and silently override that default.
166
166
  this.#permissionMode = input.permissionMode;
167
- const session = await this.#createSession(input.name ?? DEFAULT_SESSION_NAME);
167
+ const session = await this.#createSession(input.name ?? DEFAULT_SESSION_NAME, input.thinkingLevel);
168
168
  return projectSessionCatalogSummary(session);
169
169
  }
170
170
  async listSessions(options = {}) {
@@ -1139,7 +1139,7 @@ class RuntimeHostMakaSessionDriverImpl {
1139
1139
  this.#sessionCreation = undefined;
1140
1140
  }
1141
1141
  }
1142
- async #createSession(name) {
1142
+ async #createSession(name, thinkingLevel = this.#thinkingLevel) {
1143
1143
  const workspace = this.#workspace.target;
1144
1144
  if (!workspace) {
1145
1145
  throw new Error('A remote Runtime Host Session requires an explicit Project');
@@ -1163,7 +1163,7 @@ class RuntimeHostMakaSessionDriverImpl {
1163
1163
  ...(this.#orchestrationMode === 'default'
1164
1164
  ? {}
1165
1165
  : { orchestrationMode: this.#orchestrationMode }),
1166
- ...(this.#thinkingLevel === undefined ? {} : { thinkingLevel: this.#thinkingLevel }),
1166
+ ...(thinkingLevel === undefined ? {} : { thinkingLevel }),
1167
1167
  }));
1168
1168
  this.#sessionGeneration += 1;
1169
1169
  this.#sessionId = sessionId;
@@ -178,6 +178,7 @@ const TUI_COPY_RESOURCES_PICKERS_EN = {
178
178
  addAccount: 'add account',
179
179
  thinkingLevels: {
180
180
  off: 'Off',
181
+ on: 'On',
181
182
  minimal: 'Minimal',
182
183
  low: 'Low',
183
184
  medium: 'Medium',
@@ -921,6 +922,7 @@ export const TUI_COPY_RESOURCES = {
921
922
  addAccount: '添加账号',
922
923
  thinkingLevels: {
923
924
  off: '关',
925
+ on: '开',
924
926
  minimal: '最小',
925
927
  low: '低',
926
928
  medium: '中',
@@ -1045,6 +1047,7 @@ export const TUI_COPY_RESOURCES = {
1045
1047
  addAccount: '新增帳號',
1046
1048
  thinkingLevels: {
1047
1049
  off: '關',
1050
+ on: '開',
1048
1051
  minimal: '最小',
1049
1052
  low: '低',
1050
1053
  medium: '中',
@@ -1,6 +1,7 @@
1
1
  // ../core/dist/model-thinking.js
2
2
  var THINKING_LEVELS = [
3
3
  "off",
4
+ "on",
4
5
  "minimal",
5
6
  "low",
6
7
  "medium",
@@ -8,7 +9,7 @@ var THINKING_LEVELS = [
8
9
  "xhigh",
9
10
  "max"
10
11
  ];
11
- var DECLARABLE_RELAY_THINKING_LEVELS = THINKING_LEVELS.filter((level) => level !== "off");
12
+ var DECLARABLE_RELAY_THINKING_LEVELS = THINKING_LEVELS.filter((level) => level !== "off" && level !== "on");
12
13
 
13
14
  // src/model-presentation.ts
14
15
  function describeAntigravityModels(models) {
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ /** Client-safe delivery contract. No filesystem or networking capabilities live here. */
20
+ export const IMAGE_DELIVERY_IDENTITY_MAX_LENGTH = 512;
21
+ export const IMAGE_DELIVERY_SOURCE_MAX_LENGTH = 4096;
22
+ /** JavaScript string length, shared by stream capture and Markdown consumers. */
23
+ export const IMAGE_MARKDOWN_MAX_LENGTH = 1024 * 1024;
24
+ export function isImageDeliverySource(value) {
25
+ return (typeof value === 'string' &&
26
+ value.length > 0 &&
27
+ value.length <= IMAGE_DELIVERY_SOURCE_MAX_LENGTH &&
28
+ !/[\x00-\x1f\x7f]/.test(value));
29
+ }
30
+ export function isRemoteImageSource(source) {
31
+ return /^https?:/i.test(source);
32
+ }
33
+ export function isImageDeliveryRequest(value) {
34
+ if (!value || typeof value !== 'object' || Array.isArray(value))
35
+ return false;
36
+ const v = value;
37
+ const identity = (s) => typeof s === 'string' &&
38
+ s.length > 0 &&
39
+ s.length <= IMAGE_DELIVERY_IDENTITY_MAX_LENGTH &&
40
+ !/[\x00-\x1f\x7f]/.test(s);
41
+ return (Object.keys(v).every((k) => ['turnId', 'messageId', 'source', 'retry', 'loadRemote'].includes(k)) &&
42
+ identity(v.turnId) &&
43
+ identity(v.messageId) &&
44
+ isImageDeliverySource(v.source) &&
45
+ (v.retry === undefined || typeof v.retry === 'boolean') &&
46
+ (v.loadRemote === undefined || typeof v.loadRemote === 'boolean'));
47
+ }
48
+ export const IMAGE_DELIVERY_FAILURES = [
49
+ 'not_found',
50
+ 'not_allowed',
51
+ 'too_large',
52
+ 'unsupported_mime',
53
+ 'quota_exceeded',
54
+ 'download_failed',
55
+ 'read_failed',
56
+ 'queue_full',
57
+ ];
58
+ export function isImageDeliveryAttempt(value) {
59
+ if (!value || typeof value !== 'object' || Array.isArray(value))
60
+ return false;
61
+ const v = value;
62
+ return (Object.keys(v).every((k) => ['status', 'reason'].includes(k)) &&
63
+ (v.status === 'pending'
64
+ ? v.reason === undefined
65
+ : v.status === 'failed' && IMAGE_DELIVERY_FAILURES.includes(v.reason)));
66
+ }
67
+ export const DEFAULT_IMAGE_ARCHIVE_LIMITS = Object.freeze({
68
+ sessionBytes: 100 * 1024 * 1024,
69
+ workspaceBytes: 1024 * 1024 * 1024,
70
+ });
71
+ export function isImageDeliveryMetadata(value) {
72
+ if (!value || typeof value !== 'object' || Array.isArray(value))
73
+ return false;
74
+ const v = value;
75
+ return (Object.keys(v).every((k) => ['messageId', 'source', 'status', 'contentSha256'].includes(k)) &&
76
+ typeof v.messageId === 'string' &&
77
+ v.messageId.length > 0 &&
78
+ v.messageId.length <= IMAGE_DELIVERY_IDENTITY_MAX_LENGTH &&
79
+ isImageDeliverySource(v.source) &&
80
+ v.status === 'ready' &&
81
+ typeof v.contentSha256 === 'string' &&
82
+ /^[a-f0-9]{64}$/.test(v.contentSha256));
83
+ }
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { Lexer, Marked } from 'marked';
20
+ import { IMAGE_MARKDOWN_MAX_LENGTH } from './image-delivery.js';
21
+ const parser = new Marked({ gfm: true });
22
+ /** Canonical image destinations in real Markdown nodes, including attachment refs.
23
+ * Consumers own their source policy; parsing grants no filesystem or network access.
24
+ */
25
+ export function markdownImageSources(text) {
26
+ return [...new Set(markdownImages(text).map((image) => image.source))];
27
+ }
28
+ /** Original token spelling and canonical destination, including reference images. */
29
+ export function markdownImages(text) {
30
+ if (!text.includes('![') || text.length > IMAGE_MARKDOWN_MAX_LENGTH)
31
+ return [];
32
+ const images = [];
33
+ parser.walkTokens(parser.lexer(text), (token) => {
34
+ if (token.type === 'image')
35
+ images.push({ raw: token.raw, source: token.href });
36
+ });
37
+ return images;
38
+ }
39
+ /** Resolve a complete inline destination, rejecting trailing or partial syntax. */
40
+ export function parseMarkdownImageDestination(source) {
41
+ const markdown = `![](${source})`;
42
+ const [token] = Lexer.lexInline(markdown, { gfm: true });
43
+ return token?.type === 'image' && token.raw === markdown ? token.href : undefined;
44
+ }
@@ -439,6 +439,14 @@ const COMMAND_CODE_MODEL_METADATA = {
439
439
  function buildStaticModelMetadata(active) {
440
440
  return {
441
441
  anthropic: ANTHROPIC_MODEL_OVERRIDES,
442
+ opencode: {
443
+ 'claude-sonnet-4-5': {
444
+ thinkingOptions: ANTHROPIC_MODEL_OVERRIDES['claude-sonnet-4-5'].thinkingOptions,
445
+ },
446
+ 'claude-haiku-4-5': {
447
+ thinkingOptions: ANTHROPIC_MODEL_OVERRIDES['claude-haiku-4-5'].thinkingOptions,
448
+ },
449
+ },
442
450
  openai: OPENAI_GPT6_MODEL_OVERRIDES,
443
451
  'claude-subscription': claudeSubscriptionModelMetadata(active),
444
452
  // The Command Code Provider-API plan rides the same effort table.
@@ -20,6 +20,7 @@ import { lookupModelMetadata } from './model-metadata.js';
20
20
  import { isModelApiProtocol } from './provider-registry.js';
21
21
  export const THINKING_LEVELS = [
22
22
  'off',
23
+ 'on',
23
24
  'minimal',
24
25
  'low',
25
26
  'medium',
@@ -27,29 +28,12 @@ export const THINKING_LEVELS = [
27
28
  'xhigh',
28
29
  'max',
29
30
  ];
30
- /**
31
- * The levels a generic-relay declaration may hold — the vocabulary the
32
- * settings surfaces offer and the one the data layer admits. `off` is the
33
- * sole exclusion: it is not an intensity tier but a *disable* wire
34
- * (`reasoning_effort: 'none'`), and no generic relay is presumed to honor
35
- * that encoding; built-in providers that support it get `off` from their own
36
- * metadata instead. `minimal` and every effort tier above are pure
37
- * intensity values — the user declaring them is the authority on what the
38
- * relay accepts.
39
- */
40
- export const DECLARABLE_RELAY_THINKING_LEVELS = THINKING_LEVELS.filter((level) => level !== 'off');
31
+ /** Relay declarations contain effort tiers; switches require a provider-specific wire. */
32
+ export const DECLARABLE_RELAY_THINKING_LEVELS = THINKING_LEVELS.filter((level) => level !== 'off' && level !== 'on');
41
33
  export function isThinkingLevel(value) {
42
34
  return typeof value === 'string' && THINKING_LEVELS.includes(value);
43
35
  }
44
- /**
45
- * Derive the user-facing thinking-level choices from a model's declared
46
- * `ThinkingOptions`. `none` (OpenAI's off effort) and declared `offBehavior`
47
- * surface as `'off'`; other effort values map to the same-named
48
- * `ThinkingLevel`. Raw `toggle` alone is intentionally not enough because some
49
- * adapters have no real disabled wire. Unknown effort values (not in
50
- * `ThinkingLevel`) are dropped. Returns `[]` for models with no declared
51
- * options (miss → no thinking menu, fallback default).
52
- */
36
+ /** Only expose controls the adapter can encode; unknown effort values are ignored. */
53
37
  function deriveThinkingChoices(options) {
54
38
  if (!options)
55
39
  return [];
@@ -59,10 +43,13 @@ function deriveThinkingChoices(options) {
59
43
  for (const effort of options.efforts ?? []) {
60
44
  if (effort === 'none')
61
45
  choices.add('off');
62
- else if (isThinkingLevel(effort))
46
+ else if (DECLARABLE_RELAY_THINKING_LEVELS.includes(effort)) {
63
47
  choices.add(effort);
64
- // Unknown effort values (not in ThinkingLevel) are dropped — add the
65
- // level to THINKING_LEVELS if a provider introduces a new effort tier.
48
+ }
49
+ }
50
+ // Legacy Claude models need an explicit enabled request and have no effort knob.
51
+ if (options.offBehavior === 'anthropic-thinking-disabled' && choices.size === 1) {
52
+ choices.add('on');
66
53
  }
67
54
  return THINKING_LEVELS.filter((level) => choices.has(level));
68
55
  }
@@ -108,12 +95,6 @@ function normalizeModelOverride(entry) {
108
95
  return undefined;
109
96
  const declared = {};
110
97
  if (Array.isArray(entry.thinkingLevels)) {
111
- // Declared levels are filtered to the declarable vocabulary, not merely
112
- // the level vocabulary: `off` is a disable-wire encoding no generic
113
- // relay is presumed to speak, and a declaration table has no business
114
- // carrying it. The codec rejects it in persisted documents for the same
115
- // reason; normalize silently drops it because it also sanitizes input
116
- // that never passed a validator (settings drafts, hand-edited tables).
117
98
  const declaredSet = new Set(entry.thinkingLevels.filter((level) => isThinkingLevel(level) &&
118
99
  DECLARABLE_RELAY_THINKING_LEVELS.includes(level)));
119
100
  if (declaredSet.size > 0) {
@@ -635,7 +635,15 @@ function optionalTimestamp(value) {
635
635
  return null;
636
636
  return value;
637
637
  }
638
- function eligibleSessions(sessions, filter) {
638
+ /** Complete read-only history corpus for plugin organizers; same privacy and
639
+ * revision rules as Recall, without the search query's scan-count cap. */
640
+ export async function listRecallHistorySessions(deps, activeSessionId) {
641
+ const privacy = validateWorkspacePrivacyContext(await deps.getPrivacyContext());
642
+ if (!privacy.ok || privacy.value.incognitoActive)
643
+ throw new Error('History is unavailable while workspace privacy cannot be verified or incognito is active');
644
+ return eligibleSessions(collapseSessionRevisions(await deps.listSessions(), activeSessionId), { includeArchived: false }, Number.POSITIVE_INFINITY);
645
+ }
646
+ function eligibleSessions(sessions, filter, limit = MAX_SESSIONS_SCANNED) {
639
647
  return sessions
640
648
  .filter((session) =>
641
649
  // Retired simulator transcripts are task records, not real history;
@@ -647,7 +655,7 @@ function eligibleSessions(sessions, filter) {
647
655
  const byTime = (right.lastMessageAt ?? 0) - (left.lastMessageAt ?? 0);
648
656
  return byTime !== 0 ? byTime : left.id.localeCompare(right.id);
649
657
  })
650
- .slice(0, MAX_SESSIONS_SCANNED);
658
+ .slice(0, limit);
651
659
  }
652
660
  async function readFacts(deps, terms, options) {
653
661
  if (!deps.searchFacts)
@@ -965,3 +965,5 @@ function inferLegacyTurnStatus(messages) {
965
965
  return 'failed';
966
966
  return 'completed';
967
967
  }
968
+ /** Durable provenance for assistant-created workers; visibility is a client preference. */
969
+ export const ASSISTANT_BACKGROUND_SESSION_LABEL = 'personal-assistant-background';
@@ -340,6 +340,7 @@ export function createDefaultSettings() {
340
340
  activeTab: 'requests',
341
341
  },
342
342
  appearance: {
343
+ showAssistantBackgroundSessions: false,
343
344
  workbarTogglePosition: 'titlebar',
344
345
  theme: 'auto',
345
346
  palette: 'default',
@@ -517,6 +518,7 @@ export function normalizeSettings(input) {
517
518
  // position; UI density is no longer a product setting.
518
519
  appearance: {
519
520
  ...appearanceWithoutLegacyFields,
521
+ showAssistantBackgroundSessions: base.appearance.showAssistantBackgroundSessions === true,
520
522
  workbarTogglePosition: base.appearance.workbarTogglePosition === 'edge' ? 'edge' : 'titlebar',
521
523
  palette: isThemePalette(base.appearance.palette) ? base.appearance.palette : 'default',
522
524
  // Same fail-closed rule as `palette` above, for the same reason: an
@@ -81,5 +81,6 @@ export function normalizeSubagentSettings(input) {
81
81
  enabled: value.enabled,
82
82
  });
83
83
  }
84
- return { presets };
84
+ const enabled = input.enabled;
85
+ return { ...(typeof enabled === 'boolean' ? { enabled } : {}), presets };
85
86
  }
@@ -63,6 +63,8 @@
63
63
  "./context-offload": "./dist/context-offload.js",
64
64
  "./tool-result-archive-evidence": "./dist/tool-result-archive-evidence.js",
65
65
  "./attachments": "./dist/attachments.js",
66
+ "./image-delivery": "./dist/image-delivery.js",
67
+ "./image-markdown": "./dist/image-markdown.js",
66
68
  "./artifacts": "./dist/artifacts.js",
67
69
  "./pet": "./dist/pet.js",
68
70
  "./skill-invocation": "./dist/skill-invocation.js",
@@ -169,6 +171,7 @@
169
171
  "./workhub-routing": "./dist/workhub-routing.js"
170
172
  },
171
173
  "dependencies": {
172
- "intl-messageformat": "11.2.15"
174
+ "intl-messageformat": "11.2.15",
175
+ "marked": "18.0.11"
173
176
  }
174
177
  }
@@ -1121,6 +1121,7 @@ export class AiSdkTurn {
1121
1121
  const requestProjection = shapedProjection;
1122
1122
  const completedProviderSteps = [];
1123
1123
  let requestMessages = messages;
1124
+ let finishFeedback;
1124
1125
  let result;
1125
1126
  let providerOutcome;
1126
1127
  let finishReason = 'stop';
@@ -1161,10 +1162,12 @@ export class AiSdkTurn {
1161
1162
  }
1162
1163
  const requestSystemPromptBase = joinPromptFragments([
1163
1164
  systemPrompt,
1165
+ finishFeedback,
1164
1166
  finalChildSummaryStep ? CHILD_STEP_BUDGET_FINALIZATION_PROMPT : undefined,
1165
1167
  toolRuntime.hasSandboxBoundaryDenial() ? SANDBOX_BOUNDARY_DENIED_FOR_TURN : undefined,
1166
1168
  sandboxBoundaryFinalizationStep ? SANDBOX_BOUNDARY_FINALIZATION_PROMPT : undefined,
1167
1169
  ]);
1170
+ finishFeedback = undefined;
1168
1171
  const codeModeCatalogPrompt = toolMode === 'code_mode'
1169
1172
  ? renderCodeModeCatalogPrompt(toolRuntime.hasSandboxBoundaryDenial()
1170
1173
  ? new Map([...nestedTools].filter(([name]) => name !== REQUEST_SANDBOX_BOUNDARY_TOOL_NAME))
@@ -1929,6 +1932,26 @@ export class AiSdkTurn {
1929
1932
  continue agentLoop;
1930
1933
  }
1931
1934
  }
1935
+ if (mayTakeAnotherStep &&
1936
+ returnedToolCalls.length === 0 &&
1937
+ this.deps.backend.beforeTurnFinish &&
1938
+ this.deps.backend.loadTurnRuntimeEvents &&
1939
+ this.deps.modelAdapter.mapFinishReason(finishReason) === 'end_turn') {
1940
+ const decision = await this.deps.backend.beforeTurnFinish({
1941
+ sessionId: this.deps.backend.sessionId,
1942
+ turnId,
1943
+ signal: turnAbortController.signal,
1944
+ });
1945
+ if (!decision.allow && !this.aborted && !this.loopStopRequested) {
1946
+ if ((await input.handoffBoundary?.(turnAbortController.signal, maxSteps === undefined ? null : maxSteps - runtimeSteps)) === 'pause') {
1947
+ this.handoffPaused = true;
1948
+ break agentLoop;
1949
+ }
1950
+ finishFeedback = decision.feedback;
1951
+ currentStepMessageId = this.deps.newId();
1952
+ continue agentLoop;
1953
+ }
1954
+ }
1932
1955
  break agentLoop;
1933
1956
  }
1934
1957
  // Refine Tool Availability against the final active set. Deferred
@@ -270,6 +270,8 @@ function createWorkspaceFilesystemExecutor(workspace) {
270
270
  const result = await workspace.readFile({
271
271
  cwd,
272
272
  path,
273
+ ...(operation.imagePurpose ? { imagePurpose: operation.imagePurpose } : {}),
274
+ ...(abortSignal ? { abortSignal } : {}),
273
275
  });
274
276
  if ('bytes' in result) {
275
277
  return { kind: 'read_image', bytes: result.bytes, mimeType: result.mimeType };
@@ -30,7 +30,7 @@ import { readPage } from '../read-page.js';
30
30
  import { formatJsonText } from '../format-json.js';
31
31
  import { createEditUnifiedDiff, createUnifiedDiff } from '../unified-diff.js';
32
32
  import { compareAndDeleteEntry, hostVisibilityAfterWrite, openStableTarget, readModifyWriteThroughHandle, StableWriteFailure, writeThroughHandle, } from '../file-stable-write.js';
33
- import { readWorkspaceFile } from '../image-file.js';
33
+ import { ImageFileError, readWorkspaceFile } from '../image-file.js';
34
34
  import { FILESYSTEM_WORKER_PROTOCOL_VERSION, operationAccess, operationUsesDirectoryEntry, } from './protocol.js';
35
35
  import { isLikelySandboxDenial } from '../sandbox/detect.js';
36
36
  // Canonicalisation must match the sandbox the worker runs in: realpath-based
@@ -61,8 +61,10 @@ export async function executeFilesystemOperation(operation, operationBoundary, d
61
61
  switch (operation.kind) {
62
62
  case 'read': {
63
63
  const path = await resolveExistingAllowed(operation.cwd, operation.path, 'Read', 'read', operationBoundary);
64
- const file = await readWorkspaceFile(path).catch((error) => {
65
- throw operationError('filesystem_error', error instanceof Error ? error.message : 'File could not be read.');
64
+ const file = await readWorkspaceFile(path, {
65
+ ...(operation.imagePurpose ? { imagePurpose: operation.imagePurpose } : {}),
66
+ }).catch((error) => {
67
+ throw operationError(normalizeOperationError(error).code, error instanceof Error ? error.message : 'File could not be read.');
66
68
  });
67
69
  if ('bytes' in file) {
68
70
  return {
@@ -318,6 +320,8 @@ function operationError(code, message) {
318
320
  function normalizeOperationError(error) {
319
321
  if (error instanceof FilesystemOperationError)
320
322
  return error;
323
+ if (error instanceof ImageFileError)
324
+ return operationError(error.code === 'ERR_IMAGE_TOO_LARGE' ? 'image_too_large' : 'invalid_image', error.message);
321
325
  if (error instanceof StableWriteFailure) {
322
326
  return operationError(error.code, error.message);
323
327
  }
@@ -20,10 +20,9 @@ import { z } from 'zod';
20
20
  import { readContinuationSchema, readPageSchema } from '../read-page.js';
21
21
  import { validateSandboxBoundaryExpansion } from '@maka/core/sandbox-boundary';
22
22
  import { GREP_MAX_LINES, GREP_MAX_LINES_PER_FILE, GREP_MAX_MATCH_BYTES } from '../grep-search.js';
23
- // v10 adds bounded Read pages to v9's exact Grep counts; v11 adds Glob's
24
- // truncation flag to v10's result contract. Older workers cannot satisfy the
25
- // combined result contract and must be rejected at the handshake.
26
- export const FILESYSTEM_WORKER_PROTOCOL_VERSION = 11;
23
+ // v12 adds chat image reads with a bounded payload and no model dimension limit.
24
+ // Older workers cannot satisfy this read policy and must be rejected at the handshake.
25
+ export const FILESYSTEM_WORKER_PROTOCOL_VERSION = 12;
27
26
  /** The single authority on which operation kinds are writes. Shared by the
28
27
  * client (permission/identity decisions) and the worker (operation guards) so
29
28
  * the set cannot drift. */
@@ -98,6 +97,7 @@ export const FilesystemWorkerOperationSchema = z.union([
98
97
  kind: z.literal('read'),
99
98
  cwd,
100
99
  path,
100
+ imagePurpose: z.literal('chat').optional(),
101
101
  offset: z.number().int().nonnegative().optional(),
102
102
  limit: z.number().int().positive().optional(),
103
103
  continuation: readContinuationSchema.optional(),
@@ -236,6 +236,8 @@ export const FilesystemWorkerErrorCodeSchema = z.enum([
236
236
  'grep_unavailable',
237
237
  'sandbox_denied',
238
238
  'filesystem_denied',
239
+ 'image_too_large',
240
+ 'invalid_image',
239
241
  'filesystem_error',
240
242
  // The worker may have applied the mutation before it lost the ability to
241
243
  // report back (e.g. it wrote the file then the post-write identity check
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { createBoundaryFilesystemExecutor, } from './filesystem-executor.js';
20
+ import { createLocalWorkspaceExecutor } from './workspace-executor.js';
21
+ import { ImageFileError, imageFileFailureReason } from './image-file.js';
22
+ import { FilesystemWorkerClientError } from './filesystem-worker/client.js';
23
+ import { SandboxCommandError } from './sandbox/errors.js';
24
+ /** Normalizes filesystem backends at the image reader boundary. */
25
+ export class ImageFileReadError extends Error {
26
+ reason;
27
+ constructor(reason, cause) {
28
+ super(cause instanceof Error ? cause.message : `Image read failed: ${reason}`, { cause });
29
+ this.reason = reason;
30
+ this.name = 'ImageFileReadError';
31
+ }
32
+ }
33
+ function readFailure(error) {
34
+ if (error instanceof ImageFileError)
35
+ return imageFileFailureReason(error);
36
+ if (error instanceof SandboxCommandError)
37
+ return 'not_allowed';
38
+ const code = error instanceof FilesystemWorkerClientError
39
+ ? error.reason
40
+ : error?.code;
41
+ switch (code) {
42
+ case 'image_too_large':
43
+ return 'too_large';
44
+ case 'invalid_image':
45
+ return 'unsupported_mime';
46
+ case 'ENOENT':
47
+ case 'ENOTDIR':
48
+ case 'not_found':
49
+ return 'not_found';
50
+ case 'EACCES':
51
+ case 'EPERM':
52
+ case 'filesystem_denied':
53
+ case 'path_denied':
54
+ case 'sandbox_denied':
55
+ case 'sandbox_required':
56
+ case 'sandbox_boundary_required':
57
+ return 'not_allowed';
58
+ default:
59
+ return 'read_failed';
60
+ }
61
+ }
62
+ /** The same filesystem boundary as Read, without model snapshots or tool execution. */
63
+ export function createImageFileReader(options = {}) {
64
+ const filesystem = createBoundaryFilesystemExecutor({
65
+ workspace: options.executor ?? createLocalWorkspaceExecutor(),
66
+ ...(options.filesystemWorker ? { worker: options.filesystemWorker } : {}),
67
+ ...(options.permissionProfile ? { permissionProfile: options.permissionProfile } : {}),
68
+ });
69
+ return async (input) => {
70
+ const { path, ...context } = input;
71
+ const result = await filesystem
72
+ .execute({
73
+ ...context,
74
+ operation: { kind: 'read', path, imagePurpose: 'chat' },
75
+ })
76
+ .catch((error) => {
77
+ throw new ImageFileReadError(readFailure(error), error);
78
+ });
79
+ if (result.kind !== 'read_image')
80
+ throw new ImageFileReadError('unsupported_mime');
81
+ return { bytes: result.bytes, mimeType: result.mimeType };
82
+ };
83
+ }