@openma/deepseek-harness-acp 0.4.0 → 0.4.2

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/dist/bridge.js CHANGED
@@ -265,8 +265,14 @@ var SessionProjection = class {
265
265
  toolCalls = /* @__PURE__ */ new Map();
266
266
  window = emptyWindow();
267
267
  lastContextUse;
268
- constructor(contextWindow) {
268
+ /** Client renders `_meta.terminal_output` display terminals (Zed extension). */
269
+ terminalOutput;
270
+ /** Session working directory, advertised on display terminals. */
271
+ cwd;
272
+ constructor(contextWindow, options) {
269
273
  this.contextWindow = contextWindow;
274
+ this.terminalOutput = options?.terminalOutput ?? false;
275
+ this.cwd = options?.cwd;
270
276
  }
271
277
  /** Reset per-prompt accumulators; call when a new `session/prompt` starts. */
272
278
  beginPrompt() {
@@ -395,6 +401,7 @@ var SessionProjection = class {
395
401
  const rawArguments = typeof data["arguments"] === "string" ? data["arguments"] : "{}";
396
402
  const facts = classifyToolCall(name3, rawArguments);
397
403
  this.toolCalls.set(callId, facts);
404
+ const displayTerminal = this.terminalOutput && facts.kind === "execute";
398
405
  return [
399
406
  {
400
407
  sessionUpdate: "tool_call",
@@ -404,7 +411,16 @@ var SessionProjection = class {
404
411
  kind: facts.kind,
405
412
  status: "in_progress",
406
413
  rawInput: facts.rawInput,
407
- ...facts.locations.length > 0 ? { locations: facts.locations } : {}
414
+ ...facts.locations.length > 0 ? { locations: facts.locations } : {},
415
+ ...displayTerminal ? {
416
+ content: [{ type: "terminal", terminalId: callId }],
417
+ _meta: {
418
+ terminal_info: {
419
+ terminal_id: callId,
420
+ ...this.cwd !== void 0 ? { cwd: this.cwd } : {}
421
+ }
422
+ }
423
+ } : {}
408
424
  }
409
425
  ];
410
426
  }
@@ -421,6 +437,7 @@ var SessionProjection = class {
421
437
  })() : void 0
422
438
  ) ?? asString(data["callId"]);
423
439
  if (callId === void 0) return [];
440
+ const facts = this.toolCalls.get(callId);
424
441
  const { failed, text, diffs } = extractToolResult(data);
425
442
  const content = diffs.map((diff) => ({
426
443
  type: "diff",
@@ -428,8 +445,36 @@ var SessionProjection = class {
428
445
  ...diff.oldText !== null ? { oldText: diff.oldText } : {},
429
446
  newText: diff.newText
430
447
  }));
448
+ const isCommand = facts?.kind === "execute";
449
+ if (this.terminalOutput && isCommand) {
450
+ this.toolCalls.delete(callId);
451
+ return [
452
+ ...text.length > 0 ? [
453
+ {
454
+ sessionUpdate: "tool_call_update",
455
+ toolCallId: callId,
456
+ _meta: { terminal_output: { terminal_id: callId, data: text } }
457
+ }
458
+ ] : [],
459
+ {
460
+ sessionUpdate: "tool_call_update",
461
+ toolCallId: callId,
462
+ status: failed ? "failed" : "completed",
463
+ ...text.length > 0 ? { rawOutput: { output: text, isError: failed } } : {},
464
+ _meta: {
465
+ terminal_exit: { terminal_id: callId, exit_code: failed ? 1 : 0, signal: null }
466
+ }
467
+ }
468
+ ];
469
+ }
431
470
  if (text.length > 0 && diffs.length === 0) {
432
- const block = { type: "text", text };
471
+ const block = {
472
+ type: "text",
473
+ text: isCommand ? `\`\`\`sh
474
+ ${text.replace(/\n+$/, "")}
475
+ \`\`\`
476
+ ` : text
477
+ };
433
478
  content.push({ type: "content", content: block });
434
479
  }
435
480
  this.toolCalls.delete(callId);
@@ -591,6 +636,7 @@ async function apply(ctx, config = {}) {
591
636
  const sessions = /* @__PURE__ */ new Map();
592
637
  let closed = false;
593
638
  let conn;
639
+ let clientTerminalOutput = false;
594
640
  const modelCandidates = () => {
595
641
  const seen = /* @__PURE__ */ new Set();
596
642
  if (config.model !== void 0) seen.add(config.model);
@@ -960,6 +1006,24 @@ async function apply(ctx, config = {}) {
960
1006
  };
961
1007
  const configOptions = async (record) => {
962
1008
  const result = [];
1009
+ {
1010
+ const permission = permissionService();
1011
+ const levels = permission !== void 0 && permission.names.length > 0 ? permission.names.map((name3) => {
1012
+ const spec = permission.resolve(name3);
1013
+ return { value: name3, name: spec.name, description: spec.description };
1014
+ }) : SANDBOX_MODES2.map((mode) => {
1015
+ const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
1016
+ return { value: mode, name: label.name, description: label.description };
1017
+ });
1018
+ result.push({
1019
+ type: "select",
1020
+ id: "mode",
1021
+ name: "Permissions",
1022
+ category: "mode",
1023
+ currentValue: record.modeId,
1024
+ options: levels
1025
+ });
1026
+ }
963
1027
  const models = await modelChoices(record);
964
1028
  if (models.length >= 2) {
965
1029
  result.push({
@@ -1125,7 +1189,10 @@ async function apply(ctx, config = {}) {
1125
1189
  const record = {
1126
1190
  agent,
1127
1191
  dispose,
1128
- projection: projection ?? new SessionProjection(),
1192
+ projection: projection ?? new SessionProjection(void 0, {
1193
+ terminalOutput: clientTerminalOutput,
1194
+ ...agent.session.header.cwd !== void 0 ? { cwd: agent.session.header.cwd } : {}
1195
+ }),
1129
1196
  modeId,
1130
1197
  model,
1131
1198
  approvals: modeId === "danger-full-access" ? "never" : "ask",
@@ -1195,6 +1262,8 @@ async function apply(ctx, config = {}) {
1195
1262
  initialize(params) {
1196
1263
  const hasPersistence = ctx.get("sessionPersistence") !== void 0;
1197
1264
  const requested = params.protocolVersion;
1265
+ const capsMeta = params.clientCapabilities?.["_meta"];
1266
+ clientTerminalOutput = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["terminal_output"] === true : false;
1198
1267
  return Promise.resolve({
1199
1268
  protocolVersion: typeof requested === "number" && requested >= 1 && requested < PROTOCOL_VERSION ? requested : PROTOCOL_VERSION,
1200
1269
  agentInfo: { name: "dsh-acp", title: "DeepSeek Harness", version: VERSION },
@@ -1434,6 +1503,10 @@ async function apply(ctx, config = {}) {
1434
1503
  const value = typeof params.value === "string" ? params.value : void 0;
1435
1504
  if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
1436
1505
  switch (params.configId) {
1506
+ case "mode": {
1507
+ applyMode(record, params.sessionId, value);
1508
+ break;
1509
+ }
1437
1510
  case "preset": {
1438
1511
  await switchPreset(record, params.sessionId, value);
1439
1512
  break;
package/dist/index.js CHANGED
@@ -290,8 +290,14 @@ var SessionProjection = class {
290
290
  toolCalls = /* @__PURE__ */ new Map();
291
291
  window = emptyWindow();
292
292
  lastContextUse;
293
- constructor(contextWindow) {
293
+ /** Client renders `_meta.terminal_output` display terminals (Zed extension). */
294
+ terminalOutput;
295
+ /** Session working directory, advertised on display terminals. */
296
+ cwd;
297
+ constructor(contextWindow, options) {
294
298
  this.contextWindow = contextWindow;
299
+ this.terminalOutput = options?.terminalOutput ?? false;
300
+ this.cwd = options?.cwd;
295
301
  }
296
302
  /** Reset per-prompt accumulators; call when a new `session/prompt` starts. */
297
303
  beginPrompt() {
@@ -420,6 +426,7 @@ var SessionProjection = class {
420
426
  const rawArguments = typeof data["arguments"] === "string" ? data["arguments"] : "{}";
421
427
  const facts = classifyToolCall(name3, rawArguments);
422
428
  this.toolCalls.set(callId, facts);
429
+ const displayTerminal = this.terminalOutput && facts.kind === "execute";
423
430
  return [
424
431
  {
425
432
  sessionUpdate: "tool_call",
@@ -429,7 +436,16 @@ var SessionProjection = class {
429
436
  kind: facts.kind,
430
437
  status: "in_progress",
431
438
  rawInput: facts.rawInput,
432
- ...facts.locations.length > 0 ? { locations: facts.locations } : {}
439
+ ...facts.locations.length > 0 ? { locations: facts.locations } : {},
440
+ ...displayTerminal ? {
441
+ content: [{ type: "terminal", terminalId: callId }],
442
+ _meta: {
443
+ terminal_info: {
444
+ terminal_id: callId,
445
+ ...this.cwd !== void 0 ? { cwd: this.cwd } : {}
446
+ }
447
+ }
448
+ } : {}
433
449
  }
434
450
  ];
435
451
  }
@@ -446,6 +462,7 @@ var SessionProjection = class {
446
462
  })() : void 0
447
463
  ) ?? asString(data["callId"]);
448
464
  if (callId === void 0) return [];
465
+ const facts = this.toolCalls.get(callId);
449
466
  const { failed, text, diffs } = extractToolResult(data);
450
467
  const content = diffs.map((diff) => ({
451
468
  type: "diff",
@@ -453,8 +470,36 @@ var SessionProjection = class {
453
470
  ...diff.oldText !== null ? { oldText: diff.oldText } : {},
454
471
  newText: diff.newText
455
472
  }));
473
+ const isCommand = facts?.kind === "execute";
474
+ if (this.terminalOutput && isCommand) {
475
+ this.toolCalls.delete(callId);
476
+ return [
477
+ ...text.length > 0 ? [
478
+ {
479
+ sessionUpdate: "tool_call_update",
480
+ toolCallId: callId,
481
+ _meta: { terminal_output: { terminal_id: callId, data: text } }
482
+ }
483
+ ] : [],
484
+ {
485
+ sessionUpdate: "tool_call_update",
486
+ toolCallId: callId,
487
+ status: failed ? "failed" : "completed",
488
+ ...text.length > 0 ? { rawOutput: { output: text, isError: failed } } : {},
489
+ _meta: {
490
+ terminal_exit: { terminal_id: callId, exit_code: failed ? 1 : 0, signal: null }
491
+ }
492
+ }
493
+ ];
494
+ }
456
495
  if (text.length > 0 && diffs.length === 0) {
457
- const block = { type: "text", text };
496
+ const block = {
497
+ type: "text",
498
+ text: isCommand ? `\`\`\`sh
499
+ ${text.replace(/\n+$/, "")}
500
+ \`\`\`
501
+ ` : text
502
+ };
458
503
  content.push({ type: "content", content: block });
459
504
  }
460
505
  this.toolCalls.delete(callId);
@@ -616,6 +661,7 @@ async function apply(ctx, config = {}) {
616
661
  const sessions = /* @__PURE__ */ new Map();
617
662
  let closed = false;
618
663
  let conn;
664
+ let clientTerminalOutput = false;
619
665
  const modelCandidates = () => {
620
666
  const seen = /* @__PURE__ */ new Set();
621
667
  if (config.model !== void 0) seen.add(config.model);
@@ -985,6 +1031,24 @@ async function apply(ctx, config = {}) {
985
1031
  };
986
1032
  const configOptions = async (record) => {
987
1033
  const result = [];
1034
+ {
1035
+ const permission = permissionService();
1036
+ const levels = permission !== void 0 && permission.names.length > 0 ? permission.names.map((name3) => {
1037
+ const spec = permission.resolve(name3);
1038
+ return { value: name3, name: spec.name, description: spec.description };
1039
+ }) : SANDBOX_MODES2.map((mode) => {
1040
+ const label = MODE_LABELS[mode] ?? { name: mode, description: "" };
1041
+ return { value: mode, name: label.name, description: label.description };
1042
+ });
1043
+ result.push({
1044
+ type: "select",
1045
+ id: "mode",
1046
+ name: "Permissions",
1047
+ category: "mode",
1048
+ currentValue: record.modeId,
1049
+ options: levels
1050
+ });
1051
+ }
988
1052
  const models = await modelChoices(record);
989
1053
  if (models.length >= 2) {
990
1054
  result.push({
@@ -1150,7 +1214,10 @@ async function apply(ctx, config = {}) {
1150
1214
  const record = {
1151
1215
  agent,
1152
1216
  dispose,
1153
- projection: projection ?? new SessionProjection(),
1217
+ projection: projection ?? new SessionProjection(void 0, {
1218
+ terminalOutput: clientTerminalOutput,
1219
+ ...agent.session.header.cwd !== void 0 ? { cwd: agent.session.header.cwd } : {}
1220
+ }),
1154
1221
  modeId,
1155
1222
  model,
1156
1223
  approvals: modeId === "danger-full-access" ? "never" : "ask",
@@ -1220,6 +1287,8 @@ async function apply(ctx, config = {}) {
1220
1287
  initialize(params) {
1221
1288
  const hasPersistence = ctx.get("sessionPersistence") !== void 0;
1222
1289
  const requested = params.protocolVersion;
1290
+ const capsMeta = params.clientCapabilities?.["_meta"];
1291
+ clientTerminalOutput = capsMeta !== null && typeof capsMeta === "object" ? capsMeta["terminal_output"] === true : false;
1223
1292
  return Promise.resolve({
1224
1293
  protocolVersion: typeof requested === "number" && requested >= 1 && requested < PROTOCOL_VERSION ? requested : PROTOCOL_VERSION,
1225
1294
  agentInfo: { name: "dsh-acp", title: "DeepSeek Harness", version: VERSION },
@@ -1459,6 +1528,10 @@ async function apply(ctx, config = {}) {
1459
1528
  const value = typeof params.value === "string" ? params.value : void 0;
1460
1529
  if (value === void 0) throw invalidParams(`invalid value: ${String(params.value)}`);
1461
1530
  switch (params.configId) {
1531
+ case "mode": {
1532
+ applyMode(record, params.sessionId, value);
1533
+ break;
1534
+ }
1462
1535
  case "preset": {
1463
1536
  await switchPreset(record, params.sessionId, value);
1464
1537
  break;
@@ -2061,7 +2134,7 @@ async function bootAcpProfile(host, overrides) {
2061
2134
  let rootConfigPath = join2(ownRoot(), "profile-root.cordis.yml");
2062
2135
  let bareModuleBaseUrl = pathToFileURL2(dshDir + sep2).href;
2063
2136
  const distBridge = join2(ownRoot(), "dist", "bridge.js");
2064
- const bridgeEntry = existsSync2(distBridge) ? distBridge : join2(ownRoot(), "src", "bundle.ts");
2137
+ const bridgeEntry = import.meta.url.endsWith(".ts") || !existsSync2(distBridge) ? join2(ownRoot(), "src", "bundle.ts") : distBridge;
2065
2138
  const realProfileDir = join2(home, "profiles", "acp");
2066
2139
  if (existsSync2(join2(realProfileDir, "package.json"))) {
2067
2140
  appBoot.healProfilesModuleFallback(dshPkgPath);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.0",
6
+ "version": "0.4.2",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/openma-ai/deepseek-harness-acp.git"