@xdxer/dingtalk-agent 0.1.5-beta.7 → 0.1.5-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/dist/bin/dingtalk-agent.js +118 -15
- package/dist/bin/dingtalk-agent.js.map +1 -1
- package/dist/src/agent-audit.js +108 -9
- package/dist/src/agent-audit.js.map +1 -1
- package/dist/src/development-workspace.js +37 -1
- package/dist/src/development-workspace.js.map +1 -1
- package/dist/src/dws.js +9 -0
- package/dist/src/dws.js.map +1 -1
- package/dist/src/multica-deploy.js +347 -105
- package/dist/src/multica-deploy.js.map +1 -1
- package/dist/src/multica-provider.js +151 -27
- package/dist/src/multica-provider.js.map +1 -1
- package/dist/src/multica-runtime-vocabulary.js +110 -0
- package/dist/src/multica-runtime-vocabulary.js.map +1 -0
- package/dist/src/promotion.js +2 -1
- package/dist/src/promotion.js.map +1 -1
- package/dist/src/schedule-plan.js +192 -24
- package/dist/src/schedule-plan.js.map +1 -1
- package/dist/src/skill-manager.js +76 -9
- package/dist/src/skill-manager.js.map +1 -1
- package/docs/AGENT-IN-PRODUCTION.md +255 -0
- package/docs/ARCHITECTURE.md +5 -1
- package/docs/PLATFORM-GUARDRAILS.md +188 -0
- package/docs/schemas/multica-deployment-plan.schema.json +3 -1
- package/docs/schemas/multica-deployment-receipt.schema.json +15 -1
- package/docs/schemas/multica-deployment-status.schema.json +6 -2
- package/docs/schemas/multica-workspace-inspection.schema.json +16 -0
- package/docs/schemas/multica-workspace-status.schema.json +2 -0
- package/docs/schemas/workspace-scaffold.schema.json +1 -0
- package/lab/project-workspace/fake-multica-provider.mjs +97 -7
- package/lab/project-workspace/multica-deploy.fixture.json +2 -2
- package/lab/project-workspace/multica-readonly.fixture.json +2 -2
- package/lab/project-workspace/project.fixture.json +1 -1
- package/lab/robot-eval/suite.json +1 -1
- package/package.json +5 -2
- package/skills/core/dta-agent-compose/SKILL.md +1 -1
- package/skills/core/dta-agent-compose/references/drive-and-schedules.md +22 -2
- package/skills/core/dta-basic-behavior/SKILL.md +1 -1
- package/skills/core/dta-basic-behavior/references/memory-and-evolution.md +1 -1
- package/skills/core/dta-people-group-memory/SKILL.md +4 -4
- package/skills/core/dta-people-group-memory/references/model.md +1 -1
- package/skills/platforms/multica-dingtalk/PLATFORM.md +2 -2
- package/skills/platforms/multica-dingtalk/dta-deploy-multica/SKILL.md +6 -5
- package/skills/platforms/multica-dingtalk/dta-deploy-multica/references/multica-deployment-contract.md +14 -4
- package/skills/platforms/multica-dingtalk/dta-ops-multica/SKILL.md +33 -7
- package/skills/platforms/multica-dingtalk/dta-ops-multica/scripts/multica_ext.py +118 -12
- package/dist/src/map.js +0 -157
- package/dist/src/map.js.map +0 -1
|
@@ -2,14 +2,33 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
3
|
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync, } from 'node:fs';
|
|
4
4
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
-
import { WORKSPACE_STATE_ROOT, inspectAgentProject, showDevelopmentWorkspace, } from './development-workspace.js';
|
|
5
|
+
import { RESERVED_MULTICA_ENV, WORKSPACE_STATE_ROOT, inspectAgentProject, multicaAgentIdSource, showDevelopmentWorkspace, } from './development-workspace.js';
|
|
6
6
|
import { digest, stableStringify } from './events.js';
|
|
7
|
+
import { supportedRuntimeProviders, vocabulariesForRuntimeProvider, } from './multica-runtime-vocabulary.js';
|
|
7
8
|
export const MULTICA_EVIDENCE_ROOT = '.dingtalk-agent/development-workspace-evidence';
|
|
9
|
+
/**
|
|
10
|
+
* Strip env variables the provider CLI reinterprets as mode switches (see
|
|
11
|
+
* RESERVED_MULTICA_ENV) from a child-process environment. dta reads manifest env refs from
|
|
12
|
+
* the raw env *before* this runs, so a ref named after a reserved variable still resolves —
|
|
13
|
+
* only the multica subprocess stops seeing it.
|
|
14
|
+
*/
|
|
15
|
+
export function sanitizeProviderEnv(env) {
|
|
16
|
+
const removed = RESERVED_MULTICA_ENV
|
|
17
|
+
.filter((key) => env[key] !== undefined).sort();
|
|
18
|
+
if (!removed.length)
|
|
19
|
+
return { env, removed };
|
|
20
|
+
const child = { ...env };
|
|
21
|
+
for (const key of removed)
|
|
22
|
+
delete child[key];
|
|
23
|
+
return { env: child, removed };
|
|
24
|
+
}
|
|
8
25
|
class MulticaReadFailure extends Error {
|
|
9
26
|
category;
|
|
10
|
-
|
|
27
|
+
detail;
|
|
28
|
+
constructor(category, detail) {
|
|
11
29
|
super(category);
|
|
12
30
|
this.category = category;
|
|
31
|
+
this.detail = detail;
|
|
13
32
|
}
|
|
14
33
|
}
|
|
15
34
|
export function planMulticaWorkspace(start, name, options = {}) {
|
|
@@ -151,7 +170,7 @@ export function scaffoldMulticaWorkspace(start, name, options = {}) {
|
|
|
151
170
|
},
|
|
152
171
|
};
|
|
153
172
|
}
|
|
154
|
-
const result = (applied) => ({
|
|
173
|
+
const result = (applied, createdPaths = []) => ({
|
|
155
174
|
$schema: 'dingtalk-agent/workspace-scaffold@1',
|
|
156
175
|
workspace: name,
|
|
157
176
|
provider: 'multica',
|
|
@@ -162,6 +181,7 @@ export function scaffoldMulticaWorkspace(start, name, options = {}) {
|
|
|
162
181
|
manifest: 'dingtalk-agent.json',
|
|
163
182
|
block,
|
|
164
183
|
envRefs,
|
|
184
|
+
createdPaths,
|
|
165
185
|
nextSteps: alreadyDeclared
|
|
166
186
|
? [
|
|
167
187
|
`Workspace ${name} 已声明(未改写);以下是它当前真实的配置`,
|
|
@@ -178,7 +198,10 @@ export function scaffoldMulticaWorkspace(start, name, options = {}) {
|
|
|
178
198
|
`export ${envRefs.expectedUserId}=<dws user id>`,
|
|
179
199
|
]
|
|
180
200
|
: [
|
|
181
|
-
applied
|
|
201
|
+
applied
|
|
202
|
+
? '已写入 dingtalk-agent.json;只改了本地 manifest(含 artifacts 目录),' +
|
|
203
|
+
'未创建任何 Multica 远端资源——远端 workspace/runtime 用 ops 技能供给,Agent 由 dta deploy 创建'
|
|
204
|
+
: '预览(加 --yes 落盘);本命令只写本地 manifest,不创建任何 Multica 远端资源',
|
|
182
205
|
`export ${envRefs.workspaceId}=<multica workspace uuid>`,
|
|
183
206
|
`export ${envRefs.runtimeId}=<multica runtime uuid>`,
|
|
184
207
|
`export ${envRefs.dwsProfile}=<dws profile 或 corpId>`,
|
|
@@ -219,7 +242,19 @@ export function scaffoldMulticaWorkspace(start, name, options = {}) {
|
|
|
219
242
|
renameSync(restore, manifestPath);
|
|
220
243
|
throw error;
|
|
221
244
|
}
|
|
222
|
-
|
|
245
|
+
// 声明的路径必须原子性地存在:manifest 里写了 artifacts 目录却不创建它,新项目
|
|
246
|
+
// 第一次 doctor 就挂在 storage.artifacts(E-01)——doctor 不该要求用户补脚手架遗漏。
|
|
247
|
+
// .gitkeep 让空目录进 git,fresh clone 后 doctor 依然通过。回滚路径不清理该目录
|
|
248
|
+
// (幂等无害,保持回滚逻辑简单)。
|
|
249
|
+
const artifactsDir = join(info.root, '.dingtalk-agent', 'artifacts', name);
|
|
250
|
+
mkdirSync(artifactsDir, { recursive: true });
|
|
251
|
+
const gitkeep = join(artifactsDir, '.gitkeep');
|
|
252
|
+
if (!existsSync(gitkeep))
|
|
253
|
+
writeFileSync(gitkeep, '');
|
|
254
|
+
return result(true, [
|
|
255
|
+
relative(info.root, artifactsDir).split('\\').join('/'),
|
|
256
|
+
relative(info.root, gitkeep).split('\\').join('/'),
|
|
257
|
+
]);
|
|
223
258
|
}
|
|
224
259
|
export function inspectMulticaWorkspace(start, name, options = {}) {
|
|
225
260
|
const plan = planMulticaWorkspace(start, name, options);
|
|
@@ -297,6 +332,8 @@ export function statusMulticaWorkspace(start, name, cliVersion = '') {
|
|
|
297
332
|
observedHash: report.observedHash,
|
|
298
333
|
diff: [...report.diff],
|
|
299
334
|
failures: [...report.failures],
|
|
335
|
+
failureDetails: Array.isArray(report.failureDetails) ? [...report.failureDetails] : [],
|
|
336
|
+
observedAgentRuntimeId: report.resources?.agent?.runtimeId || '',
|
|
300
337
|
remoteRead: false,
|
|
301
338
|
remoteWrite: false,
|
|
302
339
|
sideEffect: false,
|
|
@@ -304,9 +341,11 @@ export function statusMulticaWorkspace(start, name, cliVersion = '') {
|
|
|
304
341
|
};
|
|
305
342
|
}
|
|
306
343
|
function executeInspection(start, name, plan, options, persist) {
|
|
307
|
-
const
|
|
308
|
-
const target = resolveMulticaTarget(start, name,
|
|
309
|
-
const
|
|
344
|
+
const rawEnv = options.env || process.env;
|
|
345
|
+
const target = resolveMulticaTarget(start, name, rawEnv, options.cliVersion || '', options.agentIdOverride);
|
|
346
|
+
const sanitized = sanitizeProviderEnv(rawEnv);
|
|
347
|
+
const env = sanitized.env;
|
|
348
|
+
const command = options.command || rawEnv.DTA_MULTICA_BIN || 'multica';
|
|
310
349
|
const timeoutMs = positiveTimeout(options.timeoutMs);
|
|
311
350
|
if (persist) {
|
|
312
351
|
const plannedEvidence = options.out || join(MULTICA_EVIDENCE_ROOT, safeName(name), 'multica-inspections', 'planned.json');
|
|
@@ -316,6 +355,7 @@ function executeInspection(start, name, plan, options, persist) {
|
|
|
316
355
|
}
|
|
317
356
|
const calls = [];
|
|
318
357
|
const failures = [];
|
|
358
|
+
const failureDetails = [];
|
|
319
359
|
const diff = [];
|
|
320
360
|
let cliVersion = '';
|
|
321
361
|
let principalHash = '';
|
|
@@ -328,12 +368,23 @@ function executeInspection(start, name, plan, options, persist) {
|
|
|
328
368
|
const parsedConfig = parseConfig(config.stdout, target.profile);
|
|
329
369
|
serverHash = parsedConfig.server ? digest(parsedConfig.server) : '';
|
|
330
370
|
if (parsedConfig.workspaceId && parsedConfig.workspaceId !== target.workspaceId) {
|
|
331
|
-
throw new MulticaReadFailure('scope.profile-workspace-mismatch'
|
|
371
|
+
throw new MulticaReadFailure('scope.profile-workspace-mismatch', {
|
|
372
|
+
code: 'scope.profile-workspace-mismatch',
|
|
373
|
+
expected: target.workspaceId,
|
|
374
|
+
actual: safeDisplay(parsedConfig.workspaceId),
|
|
375
|
+
hint: `profile ${target.profile} 的 workspace_id 指向另一个 Workspace;` +
|
|
376
|
+
'为目标 Workspace 建专属具名 profile(复制 config 只改 workspace_id),或修正现有 profile',
|
|
377
|
+
});
|
|
332
378
|
}
|
|
333
379
|
const auth = runRead(command, profileArgs(target, ['auth', 'status']), 'auth.status', target.root, env, timeoutMs, calls);
|
|
334
380
|
const principal = parseAuth(auth.stdout + '\n' + auth.stderr);
|
|
335
381
|
if (!principal)
|
|
336
|
-
throw new MulticaReadFailure('auth.not-authenticated'
|
|
382
|
+
throw new MulticaReadFailure('auth.not-authenticated', {
|
|
383
|
+
code: 'auth.not-authenticated',
|
|
384
|
+
hint: `确认 multica --profile ${target.profile} 已登录;` +
|
|
385
|
+
'dta 调用 CLI 时已剔除保留变量(MULTICA_AGENT_ID/MULTICA_SERVER_URL/MULTICA_TOKEN),' +
|
|
386
|
+
'身份与 endpoint 只认具名 profile',
|
|
387
|
+
});
|
|
337
388
|
principalHash = digest(principal);
|
|
338
389
|
const workspaceList = jsonArray(runRead(command, profileArgs(target, ['workspace', 'list', '--output', 'json']), 'workspace.list', target.root, env, timeoutMs, calls).stdout, 'workspace list');
|
|
339
390
|
const workspaceMatches = workspaceList.filter((item) => idOf(item) === target.workspaceId);
|
|
@@ -364,6 +415,27 @@ function executeInspection(start, name, plan, options, persist) {
|
|
|
364
415
|
provider: safeDisplay(runtime.provider),
|
|
365
416
|
status: safeDisplay(runtime.status),
|
|
366
417
|
};
|
|
418
|
+
// Skill 装载合同只在注册过 trace 词汇的 runtime provider 上成立。hermes 上部署会
|
|
419
|
+
// "成功"、Skill 显示 enabled,agent 一加载却 100% `Skill not found` 且零告警——
|
|
420
|
+
// 这里 fail-closed,把确定性死局挡在任何远端写入之前。
|
|
421
|
+
const runtimeProvider = resources.runtime.provider;
|
|
422
|
+
if (!runtimeProvider) {
|
|
423
|
+
throw new MulticaReadFailure('scope.runtime-provider-unknown', {
|
|
424
|
+
code: 'scope.runtime-provider-unknown',
|
|
425
|
+
expected: supportedRuntimeProviders(),
|
|
426
|
+
actual: '(empty)',
|
|
427
|
+
hint: '远端 runtime 未报告 provider 字段;用 multica runtime get 核实后再部署',
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
if (!vocabulariesForRuntimeProvider(runtimeProvider).length) {
|
|
431
|
+
throw new MulticaReadFailure('scope.runtime-provider-unsupported', {
|
|
432
|
+
code: 'scope.runtime-provider-unsupported',
|
|
433
|
+
expected: supportedRuntimeProviders(),
|
|
434
|
+
actual: runtimeProvider,
|
|
435
|
+
hint: 'dta 的 Skill 装载合同只在这些 runtime provider 上成立' +
|
|
436
|
+
'(如 hermes 上 workspace skill 一律加载不到);换用受支持 provider 的 runtime',
|
|
437
|
+
});
|
|
438
|
+
}
|
|
367
439
|
const agents = jsonArray(runRead(command, scopedArgs(target, ['agent', 'list', '--include-archived', '--output', 'json']), 'agent.list', target.root, env, timeoutMs, calls).stdout, 'agent list');
|
|
368
440
|
resources.agentCandidates = agents
|
|
369
441
|
.filter((item) => optionalField(item.name) === target.projectName)
|
|
@@ -389,9 +461,26 @@ function executeInspection(start, name, plan, options, persist) {
|
|
|
389
461
|
const agent = jsonObject(runRead(command, scopedArgs(target, ['agent', 'get', target.agentId, '--output', 'json']), 'agent.get', target.root, env, timeoutMs, calls).stdout, 'agent get');
|
|
390
462
|
const agentWorkspace = optionalField(agent.workspace_id);
|
|
391
463
|
const agentRuntime = optionalField(agent.runtime_id);
|
|
392
|
-
if (idOf(agent) !== target.agentId || agentWorkspace !== target.workspaceId
|
|
393
|
-
|
|
394
|
-
|
|
464
|
+
if (idOf(agent) !== target.agentId || agentWorkspace !== target.workspaceId) {
|
|
465
|
+
throw new MulticaReadFailure('scope.agent-binding-mismatch', {
|
|
466
|
+
code: 'scope.agent-binding-mismatch',
|
|
467
|
+
expected: `workspace=${target.workspaceId}`,
|
|
468
|
+
actual: `workspace=${safeDisplay(agentWorkspace) || '(empty)'}`,
|
|
469
|
+
hint: 'Agent 不属于目标 Workspace;Workspace 边界不允许跨越',
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
// Runtime 归属漂移不再当 inspection 硬失败:那会把 deploy 与 --retire 一起锁死
|
|
473
|
+
// (retire 被它要解开的锁挡住)。漂移记录成 diff;apply 层负责在没有显式
|
|
474
|
+
// --rebind-runtime 时拒绝,retire 不受它阻挡。
|
|
475
|
+
if (agentRuntime !== target.runtimeId) {
|
|
476
|
+
diff.push('agent.runtime-drift');
|
|
477
|
+
failureDetails.push({
|
|
478
|
+
code: 'agent.runtime-drift',
|
|
479
|
+
expected: target.runtimeId,
|
|
480
|
+
actual: safeDisplay(agentRuntime) || '(empty)',
|
|
481
|
+
hint: '迁移 runtime 用 dta deploy --rebind-runtime(先 --dry-run);' +
|
|
482
|
+
'退役用 deploy --retire,不受漂移阻挡',
|
|
483
|
+
});
|
|
395
484
|
}
|
|
396
485
|
resources.agent = {
|
|
397
486
|
id: target.agentId,
|
|
@@ -409,7 +498,12 @@ function executeInspection(start, name, plan, options, persist) {
|
|
|
409
498
|
})).sort(compareNamedResource);
|
|
410
499
|
}
|
|
411
500
|
else if (resources.agentCandidates.length > 0) {
|
|
412
|
-
throw new MulticaReadFailure('agent.unbound-name-candidates'
|
|
501
|
+
throw new MulticaReadFailure('agent.unbound-name-candidates', {
|
|
502
|
+
code: 'agent.unbound-name-candidates',
|
|
503
|
+
actual: resources.agentCandidates.map((item) => `${item.id}${item.archived ? ' (archived)' : ''}`),
|
|
504
|
+
hint: '远端已有同名 Agent,dta 不按名字猜绑定;核对候选后在 manifest 声明 ' +
|
|
505
|
+
'provider.agentIdFrom 指向正确 ID(或依赖首次 deploy 写入的 state.binding.agentId)',
|
|
506
|
+
});
|
|
413
507
|
}
|
|
414
508
|
else {
|
|
415
509
|
diff.push('agent.create');
|
|
@@ -428,7 +522,13 @@ function executeInspection(start, name, plan, options, persist) {
|
|
|
428
522
|
}
|
|
429
523
|
}
|
|
430
524
|
catch (error) {
|
|
431
|
-
|
|
525
|
+
if (error instanceof MulticaReadFailure) {
|
|
526
|
+
failures.push(error.category);
|
|
527
|
+
if (error.detail)
|
|
528
|
+
failureDetails.push(error.detail);
|
|
529
|
+
}
|
|
530
|
+
else
|
|
531
|
+
failures.push('provider.read-failed');
|
|
432
532
|
}
|
|
433
533
|
const canonical = {
|
|
434
534
|
cliVersion,
|
|
@@ -468,6 +568,8 @@ function executeInspection(start, name, plan, options, persist) {
|
|
|
468
568
|
resources,
|
|
469
569
|
diff: [...diff].sort(),
|
|
470
570
|
failures: [...failures].sort(),
|
|
571
|
+
failureDetails,
|
|
572
|
+
envSanitized: sanitized.removed,
|
|
471
573
|
calls,
|
|
472
574
|
evidencePath,
|
|
473
575
|
remoteRead: true,
|
|
@@ -557,19 +659,22 @@ function resolveMulticaTarget(start, name, env, cliVersion, agentIdOverride = ''
|
|
|
557
659
|
blocking.push('agent-id-override.invalid');
|
|
558
660
|
}
|
|
559
661
|
}
|
|
560
|
-
else
|
|
662
|
+
else {
|
|
663
|
+
// 来源选择收口到 multicaAgentIdSource(schedule 共用同一份,防两处漂移)。
|
|
561
664
|
// A declared-but-empty agentIdFrom means "not yet bound" on a workspace whose Agent
|
|
562
665
|
// has not been created. Treat it like an omitted agentIdFrom (createable, no blocker):
|
|
563
666
|
// the deploy W4 create path saves the real ID to state, and the run/retire paths add
|
|
564
667
|
// their own `agent-id.missing` blocker. A malformed (non-empty, invalid) value still blocks.
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
668
|
+
const source = multicaAgentIdSource(workspace);
|
|
669
|
+
if (source.kind === 'declared')
|
|
670
|
+
agentId = resolveRef(source.ref, 'agent-id', true);
|
|
671
|
+
else if (source.kind === 'state') {
|
|
672
|
+
try {
|
|
673
|
+
agentId = resourceId(source.agentId, 'state.agent-id');
|
|
674
|
+
}
|
|
675
|
+
catch {
|
|
676
|
+
blocking.push('state.agent-id.invalid');
|
|
677
|
+
}
|
|
573
678
|
}
|
|
574
679
|
}
|
|
575
680
|
return {
|
|
@@ -615,7 +720,7 @@ function scopedArgs(target, args) {
|
|
|
615
720
|
function runRead(command, argv, id, cwd, env, timeoutMs, calls) {
|
|
616
721
|
const run = spawnSync(command, argv, {
|
|
617
722
|
cwd,
|
|
618
|
-
env,
|
|
723
|
+
env: sanitizeProviderEnv(env).env,
|
|
619
724
|
encoding: 'utf8',
|
|
620
725
|
timeout: timeoutMs,
|
|
621
726
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -633,10 +738,27 @@ function runRead(command, argv, id, cwd, env, timeoutMs, calls) {
|
|
|
633
738
|
stderrHash: sha256(stderr),
|
|
634
739
|
passed,
|
|
635
740
|
});
|
|
636
|
-
if (!passed)
|
|
637
|
-
|
|
741
|
+
if (!passed) {
|
|
742
|
+
emitProviderDiagnostic(id, exitCode, stderr || stdout);
|
|
743
|
+
throw new MulticaReadFailure(`command.${id}`, {
|
|
744
|
+
code: `command.${id}`,
|
|
745
|
+
actual: `exit=${exitCode}`,
|
|
746
|
+
hint: 'provider CLI 原始 stderr 已透传到终端(证据文件只存 hash)',
|
|
747
|
+
});
|
|
748
|
+
}
|
|
638
749
|
return { stdout, stderr };
|
|
639
750
|
}
|
|
751
|
+
/**
|
|
752
|
+
* Provider CLI failures used to vanish into a hash: the receipt recorded `stderrHash` and the
|
|
753
|
+
* user saw only `command.<id>`. The raw text still must never enter evidence files, so it goes
|
|
754
|
+
* straight to dta's own stderr — visible in the moment, never persisted.
|
|
755
|
+
*/
|
|
756
|
+
export function emitProviderDiagnostic(id, exitCode, output) {
|
|
757
|
+
const text = output.trim();
|
|
758
|
+
const body = !text ? '(无 stderr 输出)'
|
|
759
|
+
: text.length > 2048 ? `${text.slice(0, 2048)}…` : text;
|
|
760
|
+
process.stderr.write(`[multica ${id}] exit=${exitCode} ${body}\n`);
|
|
761
|
+
}
|
|
640
762
|
function parseMulticaVersion(value) {
|
|
641
763
|
const match = value.match(/\bmultica\s+([0-9]+\.[0-9]+\.[0-9]+(?:[-+][A-Za-z0-9.-]+)?)/i);
|
|
642
764
|
if (!match)
|
|
@@ -757,6 +879,8 @@ function emptyStatus(workspace) {
|
|
|
757
879
|
observedHash: workspace.observedHash,
|
|
758
880
|
diff: [],
|
|
759
881
|
failures: ['inspection.missing'],
|
|
882
|
+
failureDetails: [],
|
|
883
|
+
observedAgentRuntimeId: '',
|
|
760
884
|
remoteRead: false,
|
|
761
885
|
remoteWrite: false,
|
|
762
886
|
sideEffect: false,
|