maka-agent 0.2.0-dev.26.20260909 → 0.2.0-dev.28.20260911

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 (125) hide show
  1. package/LICENSE +106 -0
  2. package/dist/cli-core.js +28 -0
  3. package/dist/runtime-host-cli.js +43 -3
  4. package/dist/runtime-host-handoff-surface.js +10 -8
  5. package/dist/runtime-host-lifecycle-transaction.js +28 -3
  6. package/dist/runtime-host-local-source-retirement.js +1 -1
  7. package/dist/runtime-host-run-command.js +15 -7
  8. package/dist/runtime-host-setup-command.js +29 -6
  9. package/dist/runtime-host-update-command.js +49 -5
  10. package/dist/session-export-command.js +72 -0
  11. package/dist/session-import-command.js +69 -0
  12. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  13. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  14. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  15. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  16. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  17. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +9 -4
  18. package/node_modules/@maka/core/dist/diagnostic-log.js +18 -7
  19. package/node_modules/@maka/core/dist/text-sanitize.js +8 -2
  20. package/node_modules/@maka/core/dist/usage-stats/types.js +2 -0
  21. package/node_modules/@maka/core/dist/workhub-routing.js +126 -0
  22. package/node_modules/@maka/core/package.json +2 -1
  23. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +15 -11
  24. package/node_modules/@maka/runtime/dist/archive-read-tool.js +3 -1
  25. package/node_modules/@maka/runtime/dist/bots/bot-test.js +2 -1
  26. package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +24 -20
  27. package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +5 -3
  28. package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +87 -29
  29. package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +40 -36
  30. package/node_modules/@maka/runtime/dist/builtin-tools.js +21 -2
  31. package/node_modules/@maka/runtime/dist/computer-use-tools.js +4 -1
  32. package/node_modules/@maka/runtime/dist/deep-research-tools.js +11 -5
  33. package/node_modules/@maka/runtime/dist/edit-replace.js +2 -2
  34. package/node_modules/@maka/runtime/dist/filesystem-worker/operations.js +5 -6
  35. package/node_modules/@maka/runtime/dist/memory-extraction-evidence.js +7 -1
  36. package/node_modules/@maka/runtime/dist/memory-extraction.js +12 -7
  37. package/node_modules/@maka/runtime/dist/openai-responses-websocket.js +2 -5
  38. package/node_modules/@maka/runtime/dist/plugin-agent-service.js +131 -0
  39. package/node_modules/@maka/runtime/dist/plugin-approval-service.js +33 -0
  40. package/node_modules/@maka/runtime/dist/plugin-attachment-service.js +53 -0
  41. package/node_modules/@maka/runtime/dist/plugin-command-service.js +100 -0
  42. package/node_modules/@maka/runtime/dist/plugin-composition-loader.js +11 -2
  43. package/node_modules/@maka/runtime/dist/plugin-data-services.js +332 -0
  44. package/node_modules/@maka/runtime/dist/plugin-fs-service.js +62 -0
  45. package/node_modules/@maka/runtime/dist/plugin-goal-service.js +61 -0
  46. package/node_modules/@maka/runtime/dist/plugin-invocation-signal.js +24 -0
  47. package/node_modules/@maka/runtime/dist/plugin-llm-service.js +85 -0
  48. package/node_modules/@maka/runtime/dist/plugin-lsp-service.js +94 -0
  49. package/node_modules/@maka/runtime/dist/plugin-scope-registry.js +91 -0
  50. package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +71 -0
  51. package/node_modules/@maka/runtime/dist/plugin-shell-env-service.js +87 -0
  52. package/node_modules/@maka/runtime/dist/plugin-shell-service.js +74 -0
  53. package/node_modules/@maka/runtime/dist/plugin-skill-service.js +93 -0
  54. package/node_modules/@maka/runtime/dist/plugin-system-prompt-service.js +346 -0
  55. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +18 -52
  56. package/node_modules/@maka/runtime/dist/plugin-user-question-service.js +43 -0
  57. package/node_modules/@maka/runtime/dist/plugin-web-service.js +69 -0
  58. package/node_modules/@maka/runtime/dist/request-customization-fetch.js +14 -7
  59. package/node_modules/@maka/runtime/dist/session-export.js +178 -0
  60. package/node_modules/@maka/runtime/dist/session-import.js +100 -0
  61. package/node_modules/@maka/runtime/dist/session-manager.js +6 -1
  62. package/node_modules/@maka/runtime/dist/shell-run-manager.js +9 -6
  63. package/node_modules/@maka/runtime/dist/shell-run-tool-result.js +13 -6
  64. package/node_modules/@maka/runtime/dist/stream-graph-coordinator.js +43 -10
  65. package/node_modules/@maka/runtime/dist/stream-graph-handoff.js +6 -1
  66. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +1 -4
  67. package/node_modules/@maka/runtime/dist/system-prompt/workspace-instructions.js +11 -6
  68. package/node_modules/@maka/runtime/dist/tavily-search.js +8 -2
  69. package/node_modules/@maka/runtime/dist/text-line-window.js +49 -0
  70. package/node_modules/@maka/runtime/dist/tool-output.js +35 -12
  71. package/node_modules/@maka/runtime/dist/unified-diff.js +40 -10
  72. package/node_modules/@maka/runtime/dist/web-fetch-tool.js +4 -2
  73. package/node_modules/@maka/runtime/dist/workers/filesystem-worker.js +82 -33
  74. package/node_modules/@maka/runtime/dist/workspace-executor.js +2 -6
  75. package/node_modules/@maka/runtime/package.json +20 -1
  76. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +73 -24
  77. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +46 -12
  78. package/node_modules/@maka/runtime-host/dist/operator/setup-frame.js +31 -21
  79. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +7 -4
  80. package/node_modules/@maka/runtime-host/dist/protocol/artifact.js +2 -2
  81. package/node_modules/@maka/runtime-host/dist/protocol/index.js +10 -1
  82. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +30 -5
  83. package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +3 -2
  84. package/node_modules/@maka/runtime-host/dist/protocol/skill-catalog.js +2 -1
  85. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +25 -23
  86. package/node_modules/@maka/runtime-host/dist/server/artifact-coordinator.js +2 -0
  87. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +62 -4
  88. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +481 -11
  89. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +74 -0
  90. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +6 -1
  91. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +2 -2
  92. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +30 -0
  93. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +114 -20
  94. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +49 -12
  95. package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +234 -0
  96. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  97. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
  98. package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +17 -8
  99. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +189 -92
  100. package/node_modules/@maka/runtime-host/dist/server/runtime-resource-projection.js +3 -9
  101. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +20 -21
  102. package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +24 -2
  103. package/node_modules/@maka/runtime-host/dist/server/skill-catalog-coordinator.js +18 -2
  104. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -35
  105. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +92 -10
  106. package/node_modules/@maka/runtime-host/dist/transport/framed-byte-stream-transport.js +4 -1
  107. package/node_modules/@maka/runtime-host/dist/transport/local-ipc-framing.js +4 -2
  108. package/node_modules/@maka/runtime-host/dist/transport/peer-native.js +9 -8
  109. package/node_modules/@maka/runtime-host/dist/transport/resumable-peer-stream.js +14 -9
  110. package/node_modules/@maka/storage/dist/agent-run-store.js +54 -10
  111. package/node_modules/@maka/storage/dist/context-offload-snapshot.js +32 -10
  112. package/node_modules/@maka/storage/dist/context-value-mutation-gate.js +59 -0
  113. package/node_modules/@maka/storage/dist/execution-stores.js +1 -1
  114. package/node_modules/@maka/storage/dist/session-bundle-policy.js +909 -34
  115. package/node_modules/@maka/storage/dist/session-message-projection.js +8 -4
  116. package/node_modules/@maka/storage/dist/session-store.js +2 -2
  117. package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +18 -4
  118. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +12 -5
  119. package/node_modules/@maka/storage/dist/stable-storage.js +2 -1
  120. package/node_modules/@maka/storage/package.json +2 -0
  121. package/node_modules/@modelcontextprotocol/client/dist/src-D_zzAWoS.mjs +22 -5
  122. package/node_modules/@modelcontextprotocol/client/dist/src-NAgB4Mp8.cjs +22 -5
  123. package/node_modules/zod/v4/core/memoizer.cjs +12 -16
  124. package/node_modules/zod/v4/core/memoizer.js +12 -16
  125. package/package.json +1 -1
@@ -0,0 +1,53 @@
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 { Service } from './plugin-kernel.js';
20
+ /** Session-owned rich result publication and retrieval. */
21
+ export class PluginAttachmentService extends Service {
22
+ agents;
23
+ attachmentRuntime;
24
+ constructor(ctx, agents) {
25
+ super(ctx, 'attachments');
26
+ this.agents = agents;
27
+ }
28
+ bindRuntime(runtime) {
29
+ if (this.ctx.maka)
30
+ throw new Error('Only the Host may bind the Attachment Runtime');
31
+ if (this.attachmentRuntime)
32
+ throw new Error('Plugin Attachment Runtime is already bound');
33
+ this.attachmentRuntime = runtime;
34
+ return this.ctx.effect(() => () => {
35
+ if (this.attachmentRuntime === runtime)
36
+ this.attachmentRuntime = undefined;
37
+ }, 'attachments.bindRuntime()');
38
+ }
39
+ create(input) {
40
+ return this.runtime().create(input, this.agents.requireInvocation());
41
+ }
42
+ read(ref) {
43
+ return this.runtime().read(ref, this.agents.requireInvocation());
44
+ }
45
+ list() {
46
+ return this.runtime().list(this.agents.requireInvocation());
47
+ }
48
+ runtime() {
49
+ if (!this.attachmentRuntime)
50
+ throw new Error('Plugin Attachment Runtime is unavailable');
51
+ return this.attachmentRuntime;
52
+ }
53
+ }
@@ -0,0 +1,100 @@
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 { Service } from './plugin-kernel.js';
20
+ import { PluginScopeRegistry } from './plugin-scope-registry.js';
21
+ import { pluginIdentity, registerPluginContribution, MakaPluginRuntimeError, } from './plugin-runtime.js';
22
+ /** Scoped interactive command contribution registry. Client surfaces consume summaries only. */
23
+ export class PluginCommandService extends Service {
24
+ registry = new PluginScopeRegistry();
25
+ constructor(ctx) {
26
+ super(ctx, 'commands');
27
+ }
28
+ register(definition) {
29
+ validate(definition);
30
+ const identity = pluginIdentity(this.ctx);
31
+ return registerPluginContribution(this.ctx, `commands.register(${JSON.stringify(definition.name)})`, () => {
32
+ const rootId = identity.scopeId;
33
+ for (const key of keys(definition)) {
34
+ const existing = this.registry.get(rootId, key);
35
+ if (existing && existing.entryId !== identity.entryId)
36
+ throw new MakaPluginRuntimeError('activation_failed', `Plugin Command ${JSON.stringify(key)} is already registered by ${existing.entryId}`);
37
+ }
38
+ const entry = {
39
+ ...identity,
40
+ definition: Object.freeze({
41
+ ...definition,
42
+ aliases: Object.freeze([...(definition.aliases ?? [])]),
43
+ }),
44
+ token: Symbol(definition.name),
45
+ retired: false,
46
+ };
47
+ const disposers = keys(definition).map((key) => this.registry.publish(rootId, key, entry));
48
+ return async () => {
49
+ await Promise.all(disposers.reverse().map((dispose) => dispose()));
50
+ };
51
+ });
52
+ }
53
+ list(sessionId) {
54
+ const visible = this.registry.visible(assertSessionId(sessionId));
55
+ const seen = new Set();
56
+ return Object.freeze([...visible.values()]
57
+ .filter((entry) => !seen.has(entry) && Boolean(seen.add(entry)))
58
+ .sort((a, b) => a.definition.name.localeCompare(b.definition.name))
59
+ .map(({ definition, extensionId }) => Object.freeze({
60
+ name: definition.name,
61
+ description: definition.description,
62
+ aliases: Object.freeze([...(definition.aliases ?? [])]),
63
+ extensionId,
64
+ })));
65
+ }
66
+ async execute(name, context) {
67
+ const entry = this.registry.visible(assertSessionId(context.sessionId)).get(name);
68
+ if (!entry)
69
+ throw new Error(`Plugin Command is unavailable: ${name}`);
70
+ return await entry.definition.execute(Object.freeze({ ...context, args: Object.freeze([...context.args]) }));
71
+ }
72
+ inspect(rootId) {
73
+ const seen = new Set();
74
+ return Object.freeze([...this.registry.entries(rootId)]
75
+ .filter((entry) => !seen.has(entry) && Boolean(seen.add(entry)))
76
+ .sort((a, b) => a.definition.name.localeCompare(b.definition.name))
77
+ .map(({ definition, token: _token, retired: _retired, ...identity }) => Object.freeze({
78
+ ...identity,
79
+ name: definition.name,
80
+ description: definition.description,
81
+ aliases: Object.freeze([...(definition.aliases ?? [])]),
82
+ })));
83
+ }
84
+ }
85
+ function keys(definition) {
86
+ return Object.freeze([definition.name, ...(definition.aliases ?? [])]);
87
+ }
88
+ function validate(definition) {
89
+ const all = keys(definition);
90
+ if (!definition.description.trim() || typeof definition.execute !== 'function')
91
+ throw new TypeError('Plugin Command description and execute are required');
92
+ if (new Set(all).size !== all.length ||
93
+ all.some((name) => !/^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/u.test(name)))
94
+ throw new TypeError('Plugin Command names and aliases are invalid');
95
+ }
96
+ function assertSessionId(value) {
97
+ if (!value || /[\0\r\n]/u.test(value))
98
+ throw new TypeError('Session id is invalid');
99
+ return value;
100
+ }
@@ -16,6 +16,7 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
+ /// <reference lib="es2023.collection" />
19
20
  import { Context } from './plugin-kernel.js';
20
21
  import { fiberStateName, MakaPluginRuntimeError, MakaPluginTransactionBuffer, validateCompositionEntry, validatePluginPackage, validatePluginRootId, } from './plugin-runtime.js';
21
22
  const FIBER_PENDING = 0;
@@ -25,7 +26,13 @@ export class MakaCompositionLoader {
25
26
  #packages = new Map();
26
27
  #roots = new Map();
27
28
  #entries = new Map();
29
+ // Contexts and services can outlive their entries, so intern by symbol lifetime.
28
30
  #isolationLabels = new Map();
31
+ #isolationLabelFinalizer = new FinalizationRegistry(({ label, reference }) => {
32
+ // An old finalizer must not evict a newer incarnation of the same label.
33
+ if (this.#isolationLabels.get(label) === reference)
34
+ this.#isolationLabels.delete(label);
35
+ });
29
36
  #transaction;
30
37
  #compositionGeneration = 0;
31
38
  #fiberGeneration = 0;
@@ -675,10 +682,12 @@ export class MakaCompositionLoader {
675
682
  });
676
683
  }
677
684
  #isolationLabel(label) {
678
- let symbol = this.#isolationLabels.get(label);
685
+ let symbol = this.#isolationLabels.get(label)?.deref();
679
686
  if (!symbol) {
680
687
  symbol = Symbol(label);
681
- this.#isolationLabels.set(label, symbol);
688
+ const reference = new WeakRef(symbol);
689
+ this.#isolationLabels.set(label, reference);
690
+ this.#isolationLabelFinalizer.register(symbol, { label, reference });
682
691
  }
683
692
  return symbol;
684
693
  }
@@ -0,0 +1,332 @@
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 { Service } from './plugin-kernel.js';
20
+ import { pluginIdentity, registerPluginContribution } from './plugin-runtime.js';
21
+ class PluginNamespacedDataService extends Service {
22
+ domain;
23
+ dataRuntime;
24
+ constructor(ctx, name, domain) {
25
+ super(ctx, name);
26
+ this.domain = domain;
27
+ }
28
+ bindRuntime(runtime) {
29
+ if (this.ctx.maka)
30
+ throw new Error(`Only the Host may bind the Plugin ${this.domain} Runtime`);
31
+ if (this.dataRuntime)
32
+ throw new Error(`Plugin ${this.domain} Runtime is already bound`);
33
+ this.dataRuntime = runtime;
34
+ return this.ctx.effect(() => () => {
35
+ if (this.dataRuntime === runtime)
36
+ this.dataRuntime = undefined;
37
+ }, `${this.domain}.bindRuntime()`);
38
+ }
39
+ runtime() {
40
+ if (!this.dataRuntime)
41
+ throw new Error(`Plugin ${this.domain} Runtime is unavailable`);
42
+ return this.dataRuntime;
43
+ }
44
+ namespace() {
45
+ const { extensionId, scopeId } = pluginIdentity(this.ctx);
46
+ return Object.freeze({ extensionId, scopeId });
47
+ }
48
+ readValue(key) {
49
+ return this.runtime().read(this.namespace(), this.domain, validKey(key));
50
+ }
51
+ listValues(prefix) {
52
+ return this.runtime().list(this.namespace(), this.domain, prefix === undefined ? undefined : validPrefix(prefix));
53
+ }
54
+ mutateValues(mutations) {
55
+ return this.runtime().mutate(this.namespace(), this.domain, Object.freeze(mutations.map((mutation) => Object.freeze({
56
+ ...mutation,
57
+ key: validKey(mutation.key),
58
+ ...(mutation.value === undefined ? {} : { value: detached(mutation.value) }),
59
+ }))));
60
+ }
61
+ watchValues(listener) {
62
+ const subscribe = this.runtime().subscribe;
63
+ if (!subscribe)
64
+ throw new Error(`Plugin ${this.domain} subscriptions are unavailable`);
65
+ return subscribe(this.namespace(), this.domain, listener);
66
+ }
67
+ }
68
+ /** Per-extension Settings with schema contributions, CAS revisions, and no global-policy access. */
69
+ export class PluginSettingsService extends PluginNamespacedDataService {
70
+ definitions = new Map();
71
+ constructor(ctx) {
72
+ super(ctx, 'settings', 'settings');
73
+ }
74
+ define(definition) {
75
+ validateSettingDefinition(definition);
76
+ const identity = pluginIdentity(this.ctx);
77
+ return registerPluginContribution(this.ctx, `settings.define(${JSON.stringify(definition.key)})`, () => {
78
+ const id = `${identity.scopeId}\0${identity.extensionId}\0${definition.key}`;
79
+ if (this.definitions.has(id))
80
+ throw new Error(`Plugin Setting is already defined: ${definition.key}`);
81
+ this.definitions.set(id, Object.freeze({
82
+ ...definition,
83
+ ...(definition.defaultValue === undefined
84
+ ? {}
85
+ : { defaultValue: detached(definition.defaultValue) }),
86
+ }));
87
+ return () => {
88
+ this.definitions.delete(id);
89
+ };
90
+ });
91
+ }
92
+ async get(key) {
93
+ const snapshot = await this.readValue(key);
94
+ if (snapshot.value !== undefined)
95
+ return snapshot;
96
+ const definition = this.definition(key);
97
+ return definition?.defaultValue === undefined
98
+ ? snapshot
99
+ : Object.freeze({
100
+ revision: snapshot.revision,
101
+ value: detached(definition.defaultValue),
102
+ });
103
+ }
104
+ async set(key, value, options = {}) {
105
+ const definition = this.definition(key);
106
+ if (!definition)
107
+ throw new Error(`Plugin Setting is not defined: ${key}`);
108
+ if (definition.secret)
109
+ throw new Error(`Secret Setting must use ctx.authorization: ${key}`);
110
+ if (definition.validate && !definition.validate(value))
111
+ throw new TypeError(`Plugin Setting failed validation: ${key}`);
112
+ const result = await this.mutateValues([{ key, value, ...options }]);
113
+ return result[key];
114
+ }
115
+ async delete(key, options = {}) {
116
+ const result = await this.mutateValues([{ key, ...options }]);
117
+ return result[key];
118
+ }
119
+ list(prefix) {
120
+ return this.listValues(prefix);
121
+ }
122
+ watch(listener) {
123
+ return this.watchValues(listener);
124
+ }
125
+ definition(key) {
126
+ const identity = pluginIdentity(this.ctx);
127
+ return this.definitions.get(`${identity.scopeId}\0${identity.extensionId}\0${validKey(key)}`);
128
+ }
129
+ }
130
+ /** Per-extension durable KV/blob-safe JSON surface with atomic batch mutations. */
131
+ export class PluginStorageService extends PluginNamespacedDataService {
132
+ constructor(ctx) {
133
+ super(ctx, 'storage', 'storage');
134
+ }
135
+ get(key) {
136
+ return this.readValue(key);
137
+ }
138
+ async set(key, value, options = {}) {
139
+ const result = await this.mutateValues([{ key, value, ...options }]);
140
+ return result[key];
141
+ }
142
+ async delete(key, options = {}) {
143
+ const result = await this.mutateValues([{ key, ...options }]);
144
+ return result[key];
145
+ }
146
+ list(prefix) {
147
+ return this.listValues(prefix);
148
+ }
149
+ transaction(mutations) {
150
+ if (!mutations.length)
151
+ throw new TypeError('Plugin Storage transaction must not be empty');
152
+ return this.mutateValues(mutations);
153
+ }
154
+ watch(listener) {
155
+ return this.watchValues(listener);
156
+ }
157
+ }
158
+ const authorizationCommit = Symbol('authorizationCommit');
159
+ /** Declared Secret Slots. Values stay in Host custody and are never enumerable. */
160
+ export class PluginCredentialService extends Service {
161
+ dataRuntime;
162
+ slots = new Map();
163
+ constructor(ctx) {
164
+ super(ctx, 'credentials');
165
+ }
166
+ bindRuntime(runtime) {
167
+ if (this.ctx.maka)
168
+ throw new Error('Only the Host may bind the Credential Runtime');
169
+ if (this.dataRuntime)
170
+ throw new Error('Plugin Credential Runtime is already bound');
171
+ this.dataRuntime = runtime;
172
+ return this.ctx.effect(() => () => {
173
+ if (this.dataRuntime === runtime)
174
+ this.dataRuntime = undefined;
175
+ }, 'credentials.bindRuntime()');
176
+ }
177
+ declare(slot) {
178
+ validateSlot(slot);
179
+ const identity = namespace(this.ctx);
180
+ const id = slotId(identity, slot.name);
181
+ return registerPluginContribution(this.ctx, `credentials.declare(${JSON.stringify(slot.name)})`, () => {
182
+ if (this.slots.has(id))
183
+ throw new Error(`Credential Slot is already declared: ${slot.name}`);
184
+ this.slots.set(id, { identity, definition: Object.freeze({ ...slot }) });
185
+ return () => {
186
+ this.slots.delete(id);
187
+ };
188
+ });
189
+ }
190
+ has(name) {
191
+ const slot = this.requireSlot(name);
192
+ return this.runtime().hasCredential(slot.identity, slot.definition.name);
193
+ }
194
+ use(name, operation) {
195
+ if (typeof operation !== 'function')
196
+ throw new TypeError('Credential use callback is required');
197
+ const slot = this.requireSlot(name);
198
+ return this.runtime().useCredential(slot.identity, slot.definition.name, operation);
199
+ }
200
+ remove(name) {
201
+ const slot = this.requireSlot(name);
202
+ return this.runtime().removeCredential(slot.identity, slot.definition.name);
203
+ }
204
+ async [authorizationCommit](name, secret, metadata) {
205
+ if (!secret)
206
+ throw new TypeError('Credential secret must not be empty');
207
+ const slot = this.requireSlot(name);
208
+ await this.runtime().commitCredential(slot.identity, slot.definition.name, secret, metadata);
209
+ }
210
+ requireSlot(name) {
211
+ const identity = namespace(this.ctx);
212
+ const slot = this.slots.get(slotId(identity, validName(name)));
213
+ if (!slot)
214
+ throw new Error(`Credential Slot is not declared: ${name}`);
215
+ return slot;
216
+ }
217
+ runtime() {
218
+ if (!this.dataRuntime)
219
+ throw new Error('Plugin Credential Runtime is unavailable');
220
+ return this.dataRuntime;
221
+ }
222
+ }
223
+ /** Effect-owned authorization flows with one cancellable attempt per Secret Slot. */
224
+ export class PluginAuthorizationService extends Service {
225
+ flows = new Map();
226
+ active = new Map();
227
+ constructor(ctx, _credentials) {
228
+ super(ctx, 'authorization');
229
+ }
230
+ register(flow) {
231
+ validateFlow(flow);
232
+ const identity = namespace(this.ctx);
233
+ const id = slotId(identity, flow.slot);
234
+ return registerPluginContribution(this.ctx, `authorization.register(${JSON.stringify(flow.slot)})`, () => {
235
+ if (this.flows.has(id))
236
+ throw new Error(`Authorization Flow is already registered: ${flow.slot}`);
237
+ this.flows.set(id, flow);
238
+ return () => {
239
+ this.active.get(id)?.abort(new Error('Authorization Flow disposed'));
240
+ this.active.delete(id);
241
+ this.flows.delete(id);
242
+ };
243
+ });
244
+ }
245
+ async begin(slot, method, signal) {
246
+ const identity = namespace(this.ctx);
247
+ const id = slotId(identity, validName(slot));
248
+ const flow = this.flows.get(id);
249
+ if (!flow)
250
+ throw new Error(`Authorization Flow is unavailable: ${slot}`);
251
+ if (this.active.has(id))
252
+ throw new Error(`Authorization is already in progress: ${slot}`);
253
+ const selected = method ?? flow.methods[0].id;
254
+ if (!flow.methods.some(({ id }) => id === selected))
255
+ throw new Error(`Authorization method is unavailable: ${selected}`);
256
+ const controller = new AbortController();
257
+ const onAbort = () => controller.abort(signal?.reason);
258
+ signal?.addEventListener('abort', onAbort, { once: true });
259
+ this.active.set(id, controller);
260
+ let committed = false;
261
+ try {
262
+ const outcome = await flow.run({
263
+ method: selected,
264
+ signal: controller.signal,
265
+ commit: async (secret, metadata) => {
266
+ await this.ctx.credentials[authorizationCommit](slot, secret, metadata);
267
+ committed = true;
268
+ },
269
+ });
270
+ if (outcome === 'authorized' && !committed)
271
+ throw new Error('Authorization Flow returned authorized without committing a credential');
272
+ return outcome;
273
+ }
274
+ finally {
275
+ signal?.removeEventListener('abort', onAbort);
276
+ if (this.active.get(id) === controller)
277
+ this.active.delete(id);
278
+ }
279
+ }
280
+ cancel(slot) {
281
+ const identity = namespace(this.ctx);
282
+ this.active.get(slotId(identity, validName(slot)))?.abort(new Error('Authorization cancelled'));
283
+ }
284
+ }
285
+ function namespace(ctx) {
286
+ const { extensionId, scopeId } = pluginIdentity(ctx);
287
+ return Object.freeze({ extensionId, scopeId });
288
+ }
289
+ function slotId(identity, name) {
290
+ return `${identity.scopeId}\0${identity.extensionId}\0${name}`;
291
+ }
292
+ function validName(value) {
293
+ if (!/^[a-z][a-z0-9]*(?:[-_.][a-z0-9]+)*$/u.test(value))
294
+ throw new TypeError(`Invalid plugin name: ${value}`);
295
+ return value;
296
+ }
297
+ function validKey(value) {
298
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9._/-]{0,255}$/u.test(value) || value.includes('..'))
299
+ throw new TypeError(`Invalid plugin data key: ${value}`);
300
+ return value;
301
+ }
302
+ function validPrefix(value) {
303
+ if (value === '')
304
+ return value;
305
+ return validKey(value);
306
+ }
307
+ function detached(value) {
308
+ const json = JSON.stringify(value);
309
+ if (json === undefined)
310
+ throw new TypeError('Plugin data must be JSON-serializable');
311
+ if (Buffer.byteLength(json, 'utf8') > 1024 * 1024)
312
+ throw new TypeError('Plugin data value exceeds 1 MiB');
313
+ return JSON.parse(json);
314
+ }
315
+ function validateSettingDefinition(value) {
316
+ validKey(value.key);
317
+ if (!value.title.trim())
318
+ throw new TypeError('Plugin Setting title must not be empty');
319
+ }
320
+ function validateSlot(value) {
321
+ validName(value.name);
322
+ if (!value.label.trim())
323
+ throw new TypeError('Credential Slot label must not be empty');
324
+ }
325
+ function validateFlow(value) {
326
+ validName(value.slot);
327
+ if (!value.label.trim() || typeof value.run !== 'function' || value.methods.length === 0)
328
+ throw new TypeError('Invalid Authorization Flow');
329
+ const ids = value.methods.map(({ id }) => validName(id));
330
+ if (new Set(ids).size !== ids.length || value.methods.some(({ label }) => !label.trim()))
331
+ throw new TypeError('Invalid Authorization methods');
332
+ }
@@ -0,0 +1,62 @@
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 { Service } from './plugin-kernel.js';
20
+ /** Full-fidelity filesystem entry point bound to Maka's canonical workspace authority. */
21
+ export class PluginFilesystemService extends Service {
22
+ agents;
23
+ filesystemRuntime;
24
+ constructor(ctx, agents) {
25
+ super(ctx, 'fs');
26
+ this.agents = agents;
27
+ }
28
+ bindRuntime(runtime) {
29
+ if (this.ctx.maka)
30
+ throw new Error('Only the Host may bind the Filesystem Runtime');
31
+ if (this.filesystemRuntime)
32
+ throw new Error('Plugin Filesystem Runtime is already bound');
33
+ this.filesystemRuntime = runtime;
34
+ return this.ctx.effect(() => () => {
35
+ if (this.filesystemRuntime === runtime)
36
+ this.filesystemRuntime = undefined;
37
+ }, 'fs.bindRuntime()');
38
+ }
39
+ execute(operation) {
40
+ if (!this.filesystemRuntime)
41
+ throw new Error('Plugin Filesystem Runtime is unavailable');
42
+ return this.filesystemRuntime.execute(operation, this.agents.requireInvocation());
43
+ }
44
+ read(path, options = {}) {
45
+ return this.execute({ kind: 'read', path, ...options });
46
+ }
47
+ write(path, content) {
48
+ return this.execute({ kind: 'write', path, content });
49
+ }
50
+ edit(path, oldString, newString) {
51
+ return this.execute({ kind: 'edit', path, oldString, newString });
52
+ }
53
+ glob(pattern, options = {}) {
54
+ return this.execute({ kind: 'glob', pattern, ...options });
55
+ }
56
+ grep(pattern, options = {}) {
57
+ return this.execute({ kind: 'grep', pattern, ...options });
58
+ }
59
+ applyPatch(patch) {
60
+ return this.execute({ kind: 'apply_patch', patch });
61
+ }
62
+ }
@@ -0,0 +1,61 @@
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 { Service } from './plugin-kernel.js';
20
+ /** Current-Session Goal facade; mutations retain Maka's Turn lease and revision authority. */
21
+ export class PluginGoalService extends Service {
22
+ agents;
23
+ goalRuntime;
24
+ constructor(ctx, agents) {
25
+ super(ctx, 'goals');
26
+ this.agents = agents;
27
+ }
28
+ bindRuntime(runtime) {
29
+ if (this.ctx.maka)
30
+ throw new Error('Only the Host may bind the Goal Runtime');
31
+ if (this.goalRuntime)
32
+ throw new Error('Plugin Goal Runtime is already bound');
33
+ this.goalRuntime = runtime;
34
+ return this.ctx.effect(() => () => {
35
+ if (this.goalRuntime === runtime)
36
+ this.goalRuntime = undefined;
37
+ }, 'goals.bindRuntime()');
38
+ }
39
+ get() {
40
+ return this.execute({ kind: 'get' });
41
+ }
42
+ create(input) {
43
+ if (!input.objective.trim())
44
+ throw new TypeError('Goal objective must not be empty');
45
+ return this.execute({ ...input, kind: 'create', objective: input.objective.trim() });
46
+ }
47
+ clear() {
48
+ return this.execute({ kind: 'clear' });
49
+ }
50
+ pause() {
51
+ return this.execute({ kind: 'pause' });
52
+ }
53
+ resume() {
54
+ return this.execute({ kind: 'resume' });
55
+ }
56
+ execute(operation) {
57
+ if (!this.goalRuntime)
58
+ throw new Error('Plugin Goal Runtime is unavailable');
59
+ return this.goalRuntime.execute(operation, this.agents.requireInvocation());
60
+ }
61
+ }
@@ -0,0 +1,24 @@
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
+ /** Preserve Host cancellation when a plugin adds its own cancellation source. */
20
+ export function pluginInvocationSignal(invocationSignal, pluginSignal) {
21
+ if (!pluginSignal || pluginSignal === invocationSignal)
22
+ return invocationSignal;
23
+ return AbortSignal.any([invocationSignal, pluginSignal]);
24
+ }