@asc-agent/runtime 0.2.1 โ†’ 0.3.1

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 (42) hide show
  1. package/README.md +25 -25
  2. package/dist/adapters/claude-code/skill.js +259 -239
  3. package/dist/adapters/gitlab/adapter.d.ts +3 -0
  4. package/dist/adapters/gitlab/adapter.js +22 -3
  5. package/dist/adapters/gitlab/client.d.ts +27 -1
  6. package/dist/adapters/gitlab/client.js +30 -0
  7. package/dist/adapters/gitlab/ports.d.ts +3 -3
  8. package/dist/adapters/jam/adapter.d.ts +14 -0
  9. package/dist/adapters/jam/adapter.js +84 -6
  10. package/dist/adapters/jam/ports.d.ts +9 -0
  11. package/dist/adapters/jam/ports.js +49 -2
  12. package/dist/adapters/local/repo.d.ts +15 -0
  13. package/dist/adapters/local/repo.js +238 -0
  14. package/dist/adapters/markdown/state-store.js +13 -1
  15. package/dist/adapters/memory/state-store.js +4 -0
  16. package/dist/cli/asc.js +485 -10
  17. package/dist/composition/propose.d.ts +19 -0
  18. package/dist/composition/propose.js +35 -0
  19. package/dist/composition/runtime.d.ts +2 -0
  20. package/dist/composition/runtime.js +41 -5
  21. package/dist/core/attach/init.d.ts +17 -0
  22. package/dist/core/attach/init.js +28 -0
  23. package/dist/core/distribution/node-runtime.d.ts +29 -0
  24. package/dist/core/distribution/node-runtime.js +35 -0
  25. package/dist/core/distribution/release.d.ts +3 -3
  26. package/dist/core/distribution/release.js +1 -1
  27. package/dist/core/monitor/investigation.d.ts +16 -0
  28. package/dist/core/monitor/investigation.js +18 -9
  29. package/dist/core/operator/contract-draft.d.ts +124 -0
  30. package/dist/core/operator/contract-draft.js +234 -0
  31. package/dist/core/operator/derive-draft.d.ts +37 -0
  32. package/dist/core/operator/derive-draft.js +216 -0
  33. package/dist/core/operator/proceed.d.ts +79 -0
  34. package/dist/core/operator/proceed.js +123 -1
  35. package/dist/core/operator/work-state.d.ts +60 -0
  36. package/dist/core/operator/work-state.js +181 -0
  37. package/dist/core/runtime/closure.d.ts +2 -2
  38. package/dist/ports/local-repo.d.ts +84 -0
  39. package/dist/ports/local-repo.js +9 -0
  40. package/dist/ports/resource-context.d.ts +7 -0
  41. package/dist/schemas/profile.d.ts +2 -2
  42. package/package.json +3 -3
package/dist/cli/asc.js CHANGED
@@ -9,17 +9,19 @@
9
9
  import { execFile, spawnSync } from 'node:child_process';
10
10
  import { parseArgs, promisify } from 'node:util';
11
11
  import { existsSync, readdirSync, realpathSync } from 'node:fs';
12
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
12
+ import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
13
13
  import { fileURLToPath } from 'node:url';
14
- import { homedir } from 'node:os';
14
+ import { homedir, userInfo } from 'node:os';
15
15
  import { basename, dirname, join, resolve } from 'node:path';
16
- import { MINIMUM_NODE_MAJOR, checkNodeRuntime, } from "../core/distribution/node-runtime.js";
16
+ import { MINIMUM_NODE_MAJOR, checkNodeRuntime, reexecWithCandidate, } from "../core/distribution/node-runtime.js";
17
+ import { RELEASE_VERSION } from "../core/distribution/release.js";
17
18
  import { GitHubClient, discoverToken } from "../adapters/github/client.js";
18
19
  import { GitHubChangeContext, GitHubInventory, GitHubResourceContext } from "../adapters/github/context.js";
19
20
  import { GitHubEventSource } from "../adapters/github/event-source.js";
20
21
  import { GitHubScm } from "../adapters/github/scm.js";
21
22
  import { MarkdownStateStore } from "../adapters/markdown/state-store.js";
22
23
  import { LocalIdentityBinding } from "../adapters/local/identity.js";
24
+ import { IDENTITY_FILE } from "./identity-config.js";
23
25
  import { TextRenderer } from "../adapters/text/renderer.js";
24
26
  import { ApprovalService } from "../core/approval/service.js";
25
27
  import { Executor } from "../core/execution/executor.js";
@@ -32,6 +34,7 @@ import { adoptionLine, judgeAdoption, migrate } from "../core/workspace/migrate.
32
34
  import { newWorkspaceId, normalizeRemote, recoverCandidates, recoverLines } from "../core/workspace/identity.js";
33
35
  import { resolveWorkspace, resolutionLine } from "../core/workspace/resolve.js";
34
36
  import { assessSetup, renderSetup } from "../core/attach/setup.js";
37
+ import { withIdentity } from "../core/attach/init.js";
35
38
  import { applySetupPlan, computeSetupPlan, renderSetupPlan, } from "../core/attach/setup-plan.js";
36
39
  import { CLAUDE_PROVIDER, CLAUDE_SCOPE, claudeBindings } from "../adapters/claude-code/binding.js";
37
40
  import { readHeartbeat } from "../adapters/claude-code/observer.js";
@@ -42,15 +45,23 @@ import { MonitorEngine } from "../core/monitor/engine.js";
42
45
  import { CoverageLedger, renderHealth } from "../core/monitor/coverage.js";
43
46
  import { evaluateHealth, healthAlertLines } from "../core/monitor/health-alerts.js";
44
47
  import { Operator } from "../core/operator/proceed.js";
48
+ import { deriveSessionContractDraft } from "../core/operator/derive-draft.js";
49
+ import { LocalRepoAdapter } from "../adapters/local/repo.js";
50
+ import { GitHubAdapter } from "../adapters/github/adapter.js";
51
+ import { GitLabAdapter } from "../adapters/gitlab/adapter.js";
52
+ import { JamAdapter } from "../adapters/jam/adapter.js";
53
+ import { statusIndicatesDone } from "../adapters/jam/ports.js";
45
54
  import { ProgressService } from "../core/operator/progress.js";
46
55
  import { composeBindings, defaultAdapters } from "../composition/registry.js";
47
- import { buildRuntimePorts, rolesFor } from "../composition/runtime.js";
56
+ import { buildRuntimePorts, closeToolClients, rolesFor } from "../composition/runtime.js";
57
+ import { proposeBindings } from "../composition/propose.js";
48
58
  import { buildEventObservation } from "../composition/observe.js";
49
59
  import { availableProfiles, planBootstrap, renderPlan } from "../core/attach/bootstrap.js";
50
60
  import { detectStableInstall, installStableRuntime, verifyStableInstall, } from "../core/distribution/runtime-install.js";
51
61
  import { readRuntimeSelection, remediationAction, remediationLines, resolveRuntimeTarget, runtimeSelectionLine, selectionPath, writeRuntimeSelection, } from "../core/distribution/runtime-select.js";
52
62
  import { portableCommand, shorthandCommand } from "../core/distribution/release.js";
53
63
  import { preflight } from "../core/operator/preflight.js";
64
+ import { DraftProvenance, issueArgs, planSessionContract, } from "../core/operator/contract-draft.js";
54
65
  import { lookupAuthority } from "../core/policy/ownership.js";
55
66
  import { renderProgress } from "../core/operator/render.js";
56
67
  import { AuditLedger, decisionLines, delegationLine, executionLines, reclaimLine, validationLines, } from "../core/runtime/audit.js";
@@ -76,7 +87,7 @@ import { LocalOperator } from "../core/operator/local-operator.js";
76
87
  import { loadIdentityMap } from "./identity-config.js";
77
88
  const USAGE = `asc โ€” Agent Session Control
78
89
 
79
- asc proceed [--session <id>] [--goal <text>] [--json]
90
+ asc proceed [--session <id>] [--work <WORK-ID>] [--goal <text>] [--json]
80
91
 
81
92
  asc inbox list [--all] [--priority P0|P1|P2] [--json]
82
93
  asc inbox show <REQUEST_ID> [--json]
@@ -121,11 +132,16 @@ const USAGE = `asc โ€” Agent Session Control
121
132
  # without --profile: report what was detected, then stop
122
133
 
123
134
  asc setup status [--json]
135
+ asc setup identity [--role controller|monitor|both] [--actor <channel:actor>]
136
+ # ์ง€๊ธˆ ์ด ์‚ฌ๋žŒ์„ ์Šน์ธ ๊ถŒํ•œ์ž๋กœ ์„ธ์šฐ๊ณ  ์žฌ๊ณ ์ •๊นŒ์ง€ ํ•œ๋‹ค
124
137
  asc setup plan [--profile <id>] [--scope local|project] [--json]
125
138
  # says what it would change โ€” changes nothing
126
139
  asc setup apply [--profile <id>] [--scope local|project] [--json]
127
140
  asc setup apply --json # non-interactive apply. stdout is a single JSON document
128
141
 
142
+ asc session plan [--id <S-ID>] [--role <role>] [--goal <text>] [--boundary <glob>...]
143
+ [--criteria <text>...] [--owner <role>] [--provenance <f>=<STATUS>[:<src>]...]
144
+ [--json] # is this draft issuable? changes nothing
129
145
  asc session issue <ID> --role <role> --goal <text> [--block <id>]
130
146
  [--parent <S-ID>] [--issued-by <principal>]
131
147
  asc session pause <S-ID> --position <t> --next <t> [--physical <id>]
@@ -189,6 +205,8 @@ Options
189
205
  --write actually write the artefacts (default: preview)
190
206
  --role planner|researcher|implementer|verifier
191
207
  --goal the single goal of this session
208
+ --work work item to investigate before proposing a contract (asc proceed)
209
+ --actor who you are, as <channel>:<actor> (asc setup identity)
192
210
  --boundary write scope (must be narrower than the Profile's)
193
211
  --exception SOFT DENY item allowed for this session only
194
212
  --criteria a verifiable done-criterion (repeatable)
@@ -410,6 +428,8 @@ export async function runAscCommand(argv, entry = 'runtime') {
410
428
  ownership: { type: 'string', multiple: true },
411
429
  verification: { type: 'string', multiple: true },
412
430
  why: { type: 'string', multiple: true },
431
+ /** ์ดˆ์•ˆ์˜ ์ถœ์ฒ˜ โ€” `<field>=<FACT|PROPOSAL|DECISION_REQUIRED>[:<source>]` (session plan). */
432
+ provenance: { type: 'string', multiple: true },
413
433
  offline: { type: 'boolean', default: false },
414
434
  id: { type: 'string' },
415
435
  intent: { type: 'string' },
@@ -458,6 +478,8 @@ export async function runAscCommand(argv, entry = 'runtime') {
458
478
  body: { type: 'string' },
459
479
  to: { type: 'string' },
460
480
  session: { type: 'string' },
481
+ work: { type: 'string' },
482
+ actor: { type: 'string' },
461
483
  position: { type: 'string' },
462
484
  next: { type: 'string' },
463
485
  done: { type: 'string', multiple: true },
@@ -500,6 +522,17 @@ export async function runAscCommand(argv, entry = 'runtime') {
500
522
  // ๋‘๋ฉด ๊ทธ์ชฝ์— ์ •์ฑ…์ด ์ƒ๊ธฐ๊ณ (C-14 ๋ถˆ๋ณ€์‹ โ‘ฆ), ๊ทธ๋Ÿฌ๋ฉด ๋‘ ์ง„์ž…์˜ ๋‹ต์ด ๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋‹ค.
501
523
  const runnable = await checkNodeRuntime(nodeRuntimeDeps());
502
524
  if (!runnable.ok) {
525
+ // ํ›„๋ณด๋ฅผ ์ด๋ฏธ ์ฐพ์•˜์œผ๋ฉด ์ฒ˜๋ฐฉ ๋Œ€์‹  ์‹คํ–‰ํ•œ๋‹ค โ€” ๊ฐ™์€ ๋ช…๋ น, ๊ฐ™์€ argv, ํ˜ธํ™˜ Node (A6).
526
+ const reexec = reexecWithCandidate(runnable, argv, {
527
+ env: process.env,
528
+ entry: fileURLToPath(import.meta.url),
529
+ spawn: (path, args, env) => {
530
+ const child = spawnSync(path, args, { stdio: 'inherit', env: env });
531
+ return { status: child.status, signal: child.signal, ...(child.error ? { error: child.error } : {}) };
532
+ },
533
+ });
534
+ if (reexec !== null)
535
+ return reexec;
503
536
  reportNodeRuntime(runnable, Boolean(values.json) || Boolean(values.agent));
504
537
  return 1;
505
538
  }
@@ -986,7 +1019,9 @@ function declaredPolicies(resolved) {
986
1019
  }
987
1020
  return declared;
988
1021
  }
989
- const ASC_VERSION = '0.2.1';
1022
+ // RELEASE_VERSION ์ด ์ •๋ณธ์ด๋‹ค โ€” ์—ฌ๊ธฐ ๋ฌธ์ž์—ด์„ ๋”ฐ๋กœ ๋‘๋ฉด ๋ฆด๋ฆฌ์Šค๋งˆ๋‹ค ๋‚ก๋Š”๋‹ค (0.3.0 ์—์„œ
1023
+ // 0.2.1 ๋กœ ๋‚จ์•„ lock ์˜ ascVersion ํ‘œ๊ธฐ๊ฐ€ ์‹ค์ œ์™€ ์–ด๊ธ‹๋‚ฌ๋‹ค).
1024
+ const ASC_VERSION = RELEASE_VERSION;
990
1025
  const CAPABILITIES = ['scm.github', 'state.markdown', 'approval.local'];
991
1026
  const ADAPTER_VERSIONS = { 'scm.github': ASC_VERSION, 'state.markdown': ASC_VERSION };
992
1027
  /**
@@ -1075,6 +1110,8 @@ async function runSetup(command, values, entry = 'runtime') {
1075
1110
  // plan/apply๋Š” ๊ฐ™์€ ํŒ๋‹จ์„ ๋‚˜๋ˆ  ์“ด๋‹ค (C-14 ยง6). `--agent` ๋Š” apply์˜ ๋น„๋Œ€ํ™” ํ˜•ํƒœ๋‹ค.
1076
1111
  if (command === 'plan' || command === 'apply')
1077
1112
  return runSetupLifecycle(command, values, entry);
1113
+ if (command === 'identity')
1114
+ return runSetupIdentity(values);
1078
1115
  if (values.agent)
1079
1116
  return runSetupLifecycle('apply', values, entry);
1080
1117
  if (command !== undefined && command !== 'status') {
@@ -1173,6 +1210,104 @@ const nodeProcessRunner = async (command, args) => {
1173
1210
  * ์‚ฌ๋žŒ์ด ๋ณด๋Š” ์ค„๊ณผ agent๊ฐ€ ํŒŒ์‹ฑํ•˜๋Š” JSON์€ **๊ฐ™์€ plan**์—์„œ ๋‚˜์˜จ๋‹ค (C-14 ๋ถˆ๋ณ€์‹ โ‘ ).
1174
1211
  * agent ๊ฒฝ๋กœ์˜ stdout์€ JSON ๋ฌธ์„œ ํ•˜๋‚˜๋ฟ์ด๊ณ , ๊ทธ ๋ฐ–์˜ ๋ง์€ stderr๋กœ ๊ฐ„๋‹ค (ยง7).
1175
1212
  */
1213
+ /**
1214
+ * `asc setup identity` โ€” ์ง€๊ธˆ ์ด ์‚ฌ๋žŒ์„ ์ด workspace ์˜ ์Šน์ธ ๊ถŒํ•œ์ž๋กœ ์„ธ์šด๋‹ค (P1-F).
1215
+ *
1216
+ * ๊ฒฐ์ •์€ ์‚ฌ๋žŒ์ด ํ•˜๊ณ (์ด ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ด ๊ทธ ๊ฒฐ์ •์ด๋‹ค), **ํŒŒ์ผ ํŽธ์ง‘์€ ASC ๊ฐ€ ํ•œ๋‹ค.**
1217
+ * ๊ทธ ์ „๊นŒ์ง€๋Š” identities.json ๊ณผ override.json ์„ ์†์œผ๋กœ ๊ณ ์น˜๋Š” ๊ฒƒ์ด ์œ ์ผํ•œ ๊ธธ์ด์—ˆ๊ณ ,
1218
+ * ๊ทธ ๋‘˜์€ ์„œ๋กœ ๋‹ค๋ฅธ ํ˜•์‹์ด๋ผ ํ•œ์ชฝ๋งŒ ์ฑ„์›Œ ๋†“๊ณ  ์™œ ์•ˆ ์—ด๋ฆฌ๋Š”์ง€ ๋ชจ๋ฅด๋Š” ์ƒํƒœ๊ฐ€ ํ”ํ–ˆ๋‹ค.
1219
+ *
1220
+ * ๋น„๋ฐ€์€ ์ฝ์ง€๋„ ์“ฐ์ง€๋„ ์•Š๋Š”๋‹ค. ์—ฌ๊ธฐ์„œ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์€ ์ด๋ฆ„๊ณผ ์ฑ„๋„๋ฟ์ด๋‹ค.
1221
+ */
1222
+ async function runSetupIdentity(values) {
1223
+ const resolution = await resolveRoot(process.cwd(), values.root);
1224
+ if (resolution.kind === 'UNRESOLVED') {
1225
+ console.error('Not attached yet โ€” run `asc init --profile <id>` first.');
1226
+ return 2;
1227
+ }
1228
+ const root = resolution.root;
1229
+ const explicit = values.actor;
1230
+ const candidate = explicit ?? (await detectSelf());
1231
+ if (!candidate) {
1232
+ console.error('Could not tell who you are here. Pass one: --actor local:<name>');
1233
+ return 2;
1234
+ }
1235
+ const actor = candidate.includes(':') ? candidate : `local:${candidate}`;
1236
+ const name = actor.slice(actor.indexOf(':') + 1);
1237
+ const roles = values.role ?? 'both';
1238
+ if (!['controller', 'monitor', 'both'].includes(roles)) {
1239
+ console.error("--role is controller|monitor|both");
1240
+ return 2;
1241
+ }
1242
+ const asController = roles !== 'monitor';
1243
+ const asMonitor = roles !== 'controller';
1244
+ // ์–ด๋А Profile ๋กœ ์žฌ๊ณ ์ •ํ• ์ง€๋Š” lock ํŒŒ์ผ์—์„œ ์ฝ๋Š”๋‹ค. attachment ํŒ์ •์„ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ ๋Š”
1245
+ // ์ด ๋ช…๋ น ์ž์‹ ์ด drift ๋ฅผ ๋งŒ๋“ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ํ•œ ๋ฒˆ ์‹คํŒจํ•˜๋ฉด ๊ทธ ๋‹ค์Œ๋ถ€ํ„ฐ๋Š” ์ž๊ธฐ๊ฐ€ ๋‹ซ์•„์•ผ ํ• 
1246
+ // drift ๋•Œ๋ฌธ์— profile ์„ ๋ชป ์ฝ์–ด ์˜์˜ ๋ชป ๋‹ซ๋Š”๋‹ค.
1247
+ const attachedProfile = values.profile ?? (await lockedProfileId(root));
1248
+ if (!attachedProfile) {
1249
+ console.error('๋ถ™์–ด ์žˆ๋Š” Profile ์„ ์•Œ ์ˆ˜ ์—†๋‹ค โ€” `asc setup status` ๋ฅผ ๋ณด๊ณ , ํ•„์š”ํ•˜๋ฉด --profile ๋กœ ์ง€๋ชฉํ•˜๋ผ.');
1250
+ return 1;
1251
+ }
1252
+ const identitiesPath = join(root, IDENTITY_FILE);
1253
+ const overridePath = join(root, 'override.json');
1254
+ const merged = withIdentity(await readJson(identitiesPath), await readJson(overridePath), {
1255
+ name,
1256
+ actor,
1257
+ controller: asController,
1258
+ monitor: asMonitor,
1259
+ });
1260
+ await writeJson(identitiesPath, merged.identities);
1261
+ await writeJson(overridePath, merged.override);
1262
+ console.log(`${name} โ€” ${actor}`);
1263
+ console.log(` identities.json ${asController ? 'approver ๋“ฑ๋ก' : '๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์Œ'}`);
1264
+ console.log(` override.json ${asController ? 'controller.identities' : ''}${asController && asMonitor ? ' ยท ' : ''}${asMonitor ? 'monitorIdentities' : ''}`);
1265
+ // override ๋Š” lock digest ์— ๋“ค์–ด๊ฐ„๋‹ค โ€” ๊ณ ์นœ ๋’ค ์žฌ๊ณ ์ •ํ•˜์ง€ ์•Š์œผ๋ฉด ๋‹ค์Œ ๋ช…๋ น์ด ๋ฉˆ์ถ˜๋‹ค.
1266
+ // ์–ด๋А Profile ๋กœ ์žฌ๊ณ ์ •ํ• ์ง€๋Š” ์ง€๊ธˆ ๋ถ™์–ด ์žˆ๋Š” ๊ฒƒ์ด ๋‹ต์ด๋‹ค โ€” ์‚ฌ๋žŒ์—๊ฒŒ ๋‹ค์‹œ ๋ฌป์ง€ ์•Š๋Š”๋‹ค.
1267
+ const relocked = await runProfile('resolve', { ...values, profile: attachedProfile, write: true }, root);
1268
+ if (relocked !== 0)
1269
+ return relocked;
1270
+ console.log('');
1271
+ console.log(renderSetup(await inspectSetup(root)));
1272
+ return 0;
1273
+ }
1274
+ /** ์ง€๊ธˆ ๋ถ™์–ด ์žˆ๋Š” Profile id. lock ์ด ์–ด๊ธ‹๋‚˜ ์žˆ์–ด๋„ ์ฝํžŒ๋‹ค โ€” ํŒŒ์ผ์— ๊ทธ๋Œ€๋กœ ๋‚จ์•„ ์žˆ๋‹ค. */
1275
+ async function lockedProfileId(root) {
1276
+ try {
1277
+ const lock = JSON.parse(await readFile(join(root, 'profile.lock'), 'utf8'));
1278
+ return lock.profile?.id;
1279
+ }
1280
+ catch {
1281
+ return undefined;
1282
+ }
1283
+ }
1284
+ /** gitยท๊ณ„์ •์—์„œ "์ง€๊ธˆ ์ด ์‚ฌ๋žŒ"์˜ ์ด๋ฆ„๋งŒ ์ฝ๋Š”๋‹ค. ์ž๊ฒฉ ๊ฐ’์€ ์ฝ์ง€ ์•Š๋Š”๋‹ค. */
1285
+ async function detectSelf() {
1286
+ const fromGit = await execText('git', ['config', 'user.name']);
1287
+ if (fromGit)
1288
+ return fromGit;
1289
+ const user = userInfo().username;
1290
+ return user || null;
1291
+ }
1292
+ async function execText(command, args) {
1293
+ try {
1294
+ const { stdout } = await execFileAsync(command, args);
1295
+ return stdout.trim() || null;
1296
+ }
1297
+ catch {
1298
+ return null;
1299
+ }
1300
+ }
1301
+ async function readJson(path) {
1302
+ try {
1303
+ const parsed = JSON.parse(await readFile(path, 'utf8'));
1304
+ return parsed && typeof parsed === 'object' ? parsed : {};
1305
+ }
1306
+ catch {
1307
+ return {};
1308
+ }
1309
+ }
1310
+ const writeJson = (path, value) => writeFile(path, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
1176
1311
  async function runSetupLifecycle(command, values, entry) {
1177
1312
  const asJson = Boolean(values.json) || Boolean(values.agent);
1178
1313
  const plan = computeSetupPlan(await detectSetupState(values, entry));
@@ -1399,9 +1534,18 @@ async function runProfile(command, values, root) {
1399
1534
  ...(values.preset ? { presetId: values.preset } : {}),
1400
1535
  overridePath: join(root, 'override.json'),
1401
1536
  });
1402
- // ๋ฌด์—‡์ด ์‹ค์ œ๋กœ ์ œ๊ณต๋˜๋Š”์ง€๋Š” ๋ถ™์–ด ์žˆ๋Š” Adapter๊ฐ€ ์ •ํ•œ๋‹ค
1403
- const capabilities = ['scm.github', 'state.markdown', 'approval.local'];
1404
- const result = resolveRuntime(layers, capabilities, ASC_VERSION);
1537
+ // ๋ฌด์—‡์ด ์‹ค์ œ๋กœ ์ œ๊ณต๋˜๋Š”์ง€๋Š” ๋ถ™์–ด ์žˆ๋Š” Adapter๊ฐ€ ์ •ํ•œ๋‹ค โ€” ์ƒ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์‹ค์ธก์ด๋‹ค (A5).
1538
+ // discoverโ†’probe ๋ฅผ ์‹ค์ œ๋กœ ๋Œ๋ฆฐ๋‹ค: glab ๋กœ๊ทธ์ธ ๊ฐ™์€ ์ƒํƒœ ๋ณ€ํ™”๊ฐ€ resolve ์žฌ์‹คํ–‰์œผ๋กœ
1539
+ // ๋ฐ˜์˜๋œ๋‹ค (re-probe ์ˆ˜๋‹จ์ด ๋”ฐ๋กœ ํ•„์š” ์—†๋‹ค). ์‹ค์ธก ์‹คํŒจ๋Š” ๊ทธ ๊ฐˆ๋ž˜๊ฐ€ ๋น ์งˆ ๋ฟ์ด๋‹ค.
1540
+ const composed = await composeBindings({ context: { projectRoot: root, env: process.env } }).catch(() => ({ bindings: [] }));
1541
+ for (const binding of composed.bindings) {
1542
+ console.log(`Binding: ${binding.adapterId}/${binding.resource} โ€” ${binding.state}${binding.detail ? ` (${binding.detail})` : ''}`);
1543
+ }
1544
+ // lock ์˜ capability ํ‘œ๊ธฐ๋Š” ์•„์ง ์ƒ์ˆ˜๋‹ค โ€” ์‹ค์ธก์„ digest ์žฌ๋ฃŒ๋กœ ์“ฐ๋ฉด glab ๋กœ๊ทธ์ธ ์—ฌ๋ถ€์—
1545
+ // ๋”ฐ๋ผ lock ์ด ํ”๋“ค๋ฆฐ๋‹ค. ์‹ค์ธก์„ lock ์ฒด๊ณ„์— ๊ด€ํ†ต์‹œํ‚ค๋Š” ๊ฒƒ์€ P1 ๋กœ ๋„˜๊ธฐ๊ณ (์žฌ์„ค๊ณ„ ์˜์—ญ),
1546
+ // ์—ฌ๊ธฐ์„œ๋Š” probe ์‹ค์ธก์„ ์‚ฌ๋žŒ์—๊ฒŒ ๋ณด์ด๋Š” ๊ฒƒ๊นŒ์ง€ ํ•œ๋‹ค. ์‹ค์‚ฌ์šฉ read ๊ฒฝ๋กœ(proceed)๋Š”
1547
+ // buildWorkIngress ๊ฐ€ ์ด๋ฏธ composeBindings ์‹ค์ธก์œผ๋กœ ์กฐ๋ฆฝํ•œ๋‹ค.
1548
+ const result = resolveRuntime(layers, CAPABILITIES, ASC_VERSION);
1405
1549
  if (!result.ok) {
1406
1550
  console.error('resolve failed:');
1407
1551
  for (const failure of result.failures) {
@@ -1414,7 +1558,7 @@ async function runProfile(command, values, root) {
1414
1558
  const lock = buildLock({
1415
1559
  runtime,
1416
1560
  ascVersion: ASC_VERSION,
1417
- adapters: { 'scm.github': ASC_VERSION, 'state.markdown': ASC_VERSION },
1561
+ adapters: ADAPTER_VERSIONS,
1418
1562
  generatedAt,
1419
1563
  });
1420
1564
  console.log(`Layers: ${runtime.resolved.policy.layers.join(' โ†’ ')}`);
@@ -1713,11 +1857,18 @@ async function runProceed(values, store, root, resolved) {
1713
1857
  canonicalSources: (resolved?.canonicalSources ?? []).map((sourceId) => ({ sourceId })),
1714
1858
  ...(resolved?.ownership ? { ownership: resolved.ownership } : {}),
1715
1859
  });
1860
+ const workRef = values.work ?? undefined;
1861
+ const ingress = workRef ? await buildWorkIngress(store, root, sessions, resolved) : undefined;
1862
+ if (workRef && !ingress) {
1863
+ console.error(`์ž‘์—… ํ•ญ๋ชฉ '${workRef}' ์„ ์ฝ์„ ํ†ต๋กœ๊ฐ€ ์—†๋‹ค โ€” Profile bindings ์— ์ž‘์—… ํ•ญ๋ชฉ provider ๋ฅผ ์„ ์–ธํ•˜๋ผ.`);
1864
+ return 2;
1865
+ }
1716
1866
  const operator = new Operator({
1717
1867
  store,
1718
1868
  sessions,
1719
1869
  // ๋ง‰ํžŒ node๋งŒ ๋ณด๊ณ  ํŒ๋‹จํ•œ๋‹ค โ€” checkpoint๋ฅผ ๋ฐœํ–‰ํ–ˆ๋‹ค๋Š” ์ด์œ ๋กœ ๋ฉˆ์ถ”์ง€ ์•Š๋Š”๋‹ค (C-13 ยง3.1)
1720
1870
  escalations: escalationLedger(store),
1871
+ ...(ingress ? { ingress } : {}),
1721
1872
  // main()์˜ checkBootstrap๊ณผ ๊ฐ™์€ ์›์ฒœ(bootstrapGuard)์ด๋‹ค โ€” ์ค‘๋ณต ํŒ๋‹จ์ด ์•„๋‹ˆ๋ผ ๊ฐ™์€ ๋ฌธ
1722
1873
  guard: async () => {
1723
1874
  const outcome = await checkBootstrap(root);
@@ -1727,7 +1878,10 @@ async function runProceed(values, store, root, resolved) {
1727
1878
  const outcome = await operator.proceed({
1728
1879
  ...(values.session ? { sessionId: values.session } : {}),
1729
1880
  ...(values.goal ? { goal: values.goal } : {}),
1881
+ ...(workRef ? { workRef } : {}),
1730
1882
  });
1883
+ // ๋„๊ตฌ ์ž์‹(JAM MCP ์„œ๋ฒ„ ๋“ฑ)์„ ์—ฌ๊ธฐ์„œ ๋‹ซ๋Š”๋‹ค โ€” ์•ˆ ๋‹ซ์œผ๋ฉด ์ถœ๋ ฅ๊นŒ์ง€ ๋๋‚ด๊ณ ๋„ ์ข…๋ฃŒํ•˜์ง€ ๋ชปํ•œ๋‹ค.
1884
+ await closeToolClients();
1731
1885
  if (values.json) {
1732
1886
  console.log(JSON.stringify(outcome, null, 2));
1733
1887
  return outcome.kind.startsWith('BLOCKED') || outcome.kind === 'FAILED' ? 1 : 0;
@@ -1781,7 +1935,39 @@ async function runProceed(values, store, root, resolved) {
1781
1935
  console.log(` ${c.id} ${c.status.padEnd(7)} ${c.wouldDo.padEnd(8)} ${c.goal}`);
1782
1936
  }
1783
1937
  return 1;
1938
+ case 'WORK_STATE': {
1939
+ const shown = outcome.result.leaning
1940
+ ? `${outcome.result.state} (${outcome.result.leaning})`
1941
+ : outcome.result.state;
1942
+ console.log(`${outcome.workRef}: ${shown}`);
1943
+ for (const line of outcome.result.evidence)
1944
+ console.log(` ๊ทผ๊ฑฐ ${line}`);
1945
+ for (const line of outcome.result.limitations)
1946
+ console.log(` ํ•œ๊ณ„ ${line}`);
1947
+ for (const line of outcome.result.missing)
1948
+ console.log(` ๋ฏธํ™•์ธ ${line}`);
1949
+ console.log(`\n๋‹ค์Œ ํ–‰๋™: ${outcome.nextAction}`);
1950
+ return outcome.result.state === 'UNDECIDABLE' ? 1 : 0;
1951
+ }
1784
1952
  case 'PROPOSE_CONTRACT':
1953
+ if (outcome.plan) {
1954
+ console.log(`${outcome.plan.status}${outcome.full?.id ? ` โ€” ${outcome.full.id}` : ''}`);
1955
+ for (const fact of outcome.plan.facts)
1956
+ console.log(` fact ${fact.field} (${fact.source})`);
1957
+ for (const proposal of outcome.plan.proposals) {
1958
+ console.log(` proposal ${proposal.field} โ€” ${proposal.reason ?? proposal.source}`);
1959
+ }
1960
+ for (const item of outcome.plan.invalid)
1961
+ console.log(` invalid ${item.field}: ${item.detail}`);
1962
+ for (const item of outcome.plan.unresolved) {
1963
+ console.log(` decide ${item.field} [${item.reason}]: ${item.detail}`);
1964
+ }
1965
+ if (outcome.forController) {
1966
+ console.log(`\n๊ณ„์•ฝ์€ ์„ฑ๋ฆฝํ•œ๋‹ค. ๋ฐœ๊ธ‰์€ Controller ์˜ ๊ฒƒ์ด๋‹ค โ€” ${outcome.plan.issuance.detail}:`);
1967
+ console.log(` ${shorthandCommand(outcome.forController.slice(1))}`);
1968
+ }
1969
+ return outcome.plan.status === 'READY_TO_ISSUE' ? 0 : 1;
1970
+ }
1785
1971
  console.log('No runnable session. If a new contract is needed, the Controller issues it:');
1786
1972
  console.log(` asc session issue S-<date>-<n> --role ${outcome.draft.role} --goal "${outcome.draft.goal || '<goal>'}"`);
1787
1973
  return 1;
@@ -1806,6 +1992,203 @@ async function runProceed(values, store, root, resolved) {
1806
1992
  return 1;
1807
1993
  }
1808
1994
  }
1995
+ /**
1996
+ * ์ด๋ฏธ ์“ด ์„ธ์…˜ id. **ํšŒ์ˆ˜๋ผ ๋ณด๊ด€๋œ ๊ฒƒ๊นŒ์ง€ ์„ผ๋‹ค** โ€” ๊ทธ ๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ์“ฐ๋ฉด ์•ž์˜ ๊ณ„์•ฝ ๊ธฐ๋ก
1997
+ * ์œ„์— ๋‹ค๋ฅธ ๊ณ„์•ฝ์ด ์•‰๋Š”๋‹ค. ์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ๊ธฐ๋ก ํ•˜๋‚˜๋ฅผ ์žƒ์—ˆ๋‹ค.
1998
+ */
1999
+ async function usedSessionIds(store, root) {
2000
+ const active = (await store.list('session')).map((session) => session.id);
2001
+ let archived = [];
2002
+ try {
2003
+ archived = (await readdir(join(root, 'sessions', 'archive')))
2004
+ .filter((name) => name.endsWith('.md'))
2005
+ .map((name) => name.slice(0, -3));
2006
+ }
2007
+ catch {
2008
+ archived = [];
2009
+ }
2010
+ return [...new Set([...active, ...archived])];
2011
+ }
2012
+ /**
2013
+ * ์ €์žฅ์†Œ์˜ ์ตœ์ƒ์œ„ ์ž๋ฆฌ์™€ ๊ทธ ์•„๋ž˜ `src`. ๋ถ„๋ฅ˜ ์ด๋ฆ„์ด ์–ด๋А ๋ชจ๋“ˆ์— ๋งž๋Š”์ง€ ์žฌ๋Š” ์žฌ๋ฃŒ๋‹ค โ€”
2014
+ * ๋ชฉ๋ก์ผ ๋ฟ์ด๊ณ , ์ด๊ฒƒ์ด ์“ฐ๊ธฐ ๋ฒ”์œ„๊ฐ€ ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค (derive ๊ฐ€ ์ตœ์ƒ์œ„๋Š” ๋ฒ”์œ„๋กœ ์“ฐ์ง€ ์•Š๋Š”๋‹ค).
2015
+ */
2016
+ async function topLevelModules(projectRoot) {
2017
+ try {
2018
+ const entries = await readdir(projectRoot, { withFileTypes: true });
2019
+ return entries
2020
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules')
2021
+ .flatMap((entry) => [entry.name, `${entry.name}/src`]);
2022
+ }
2023
+ catch {
2024
+ return [];
2025
+ }
2026
+ }
2027
+ /** ์„ ํ–‰ยท์˜์กด์œผ๋กœ ๋ณผ ํ›„๋ณด. ๋งํฌ๊ฐ€ ๋จผ์ €๊ณ , ๋ณธ๋ฌธ์ด ๋งํ•œ ํ‚ค๊ฐ€ ๊ทธ๋‹ค์Œ์ด๋‹ค. ์ƒํ•œ์€ 5. */
2028
+ const DEPENDENCY_CAP = 5;
2029
+ function dependencyCandidates(workItem) {
2030
+ if (!workItem)
2031
+ return [];
2032
+ // ๋ณธ๋ฌธ์ด "BLOCKED BY: KEY-1" ์ฒ˜๋Ÿผ ๋งํ•œ ๊ฒƒ์€ ๋งํฌ๊ฐ€ ์—†์–ด๋„ ์˜์กด์ด๋‹ค โ€” ๋งํฌ๊ฐ€ ์—†๋‹ค๋Š”
2033
+ // ์‚ฌ์‹ค์ด ์˜์กด์ด ์—†๋‹ค๋Š” ๋œป์€ ์•„๋‹ˆ๋‹ค.
2034
+ const fromBody = new Set();
2035
+ const project = workItem.reference.split('-')[0];
2036
+ if (project) {
2037
+ const pattern = new RegExp(`(?:blocked\\s*by|์„ ํ–‰|์˜์กด)[^\\n]*?(${project}-\\d+)`, 'gi');
2038
+ for (const [, key] of `${workItem.body ?? ''}`.matchAll(pattern))
2039
+ if (key)
2040
+ fromBody.add(key);
2041
+ }
2042
+ // ๋ถ€๋ชจยทํ•˜์œ„ ์ž‘์—…์€ ํฌํ•จ ๊ด€๊ณ„์ด์ง€ ์„ ํ–‰์ด ์•„๋‹ˆ๋‹ค โ€” ๊ทธ๊ฒƒ๊นŒ์ง€ ์„ธ๋ฉด ๊ฑฐ์˜ ๋ชจ๋“  ์ž‘์—…์ด ๋ง‰ํžŒ๋‹ค.
2043
+ const ordered = [...(workItem.blockedBy ?? []), ...fromBody].filter((key) => key !== workItem.reference);
2044
+ return [...new Set(ordered)].slice(0, DEPENDENCY_CAP);
2045
+ }
2046
+ /**
2047
+ * ์ž‘์—… ํ•ญ๋ชฉ ํ•˜๋‚˜๋ฅผ ์กฐ์‚ฌํ•ด ๊ณ„์•ฝ๊นŒ์ง€ ์ž‡๋Š” ํ†ต๋กœ (P0-D).
2048
+ *
2049
+ * ์—ฌ๊ธฐ์„œ ํŒ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋ชจ์œผ๊ณ (gatherยทobserveRepo), ๋„์ถœํ•˜๊ณ (derive), **๊ธฐ์กด ํŒ์ •๊ธฐ์—
2050
+ * ๋„˜๊ธด๋‹ค**(plan = planSessionContract, issue = SessionRuntime). ๋ฒ”์œ„ยท์ฑ…์ž„ยท๋ฐœ๊ธ‰ ๊ถŒํ•œ์€
2051
+ * ๊ทธ๊ฒƒ๋“ค์ด ๋‹ตํ•˜๋Š” ๊ฒƒ์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค.
2052
+ *
2053
+ * buildMonitorEngine ์„ ๊ฑฐ์น˜์ง€ ์•Š๋Š” ์ด์œ : ๊ทธ ํ•จ์ˆ˜๋Š” GitHub ํ† ํฐ์ด ์—†์œผ๋ฉด ๋ฉˆ์ถ˜๋‹ค. ๊ฐ์‹œ์—๋Š”
2054
+ * ๋งž๋Š” ๋ฌธ์ด์ง€๋งŒ, GitLabยทJAM ํ”„๋กœ์ ํŠธ์—์„œ ์ €์žฅ์†Œ ์กฐ์‚ฌ๊นŒ์ง€ ๋ง‰์•„ ๋ฒ„๋ฆฐ๋‹ค โ€” ๊ทธ๊ฒƒ์ด "์›๊ฒฉ์ด
2055
+ * ๋ง‰ํ˜”์œผ๋‹ˆ ์ €์žฅ์†Œ๋„ ๋ชป ๋ณธ๋‹ค"๋Š” ์ž˜๋ชป๋œ ๊ฒฐ๋ก ์„ ๋งŒ๋“  ๊ตฌ์กฐ๋‹ค.
2056
+ */
2057
+ async function buildWorkIngress(store, root, runtime, resolved) {
2058
+ const { root: projectRoot } = await discoverProjectRoot(process.cwd());
2059
+ // JAM ์€ ์•„์ง `jam` ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๊น”์ง€ ์•Š๋Š” ์„ค์น˜๊ฐ€ ๊ธฐ๋ณธ์ด๋‹ค โ€” ๊ทธ ๊ฒฝ์šฐ launcher ๋ฅผ ํ†ตํ•ด ๋ถ€๋ฅธ๋‹ค.
2060
+ const jamCommand = process.env.ASC_JAM_PATH ? undefined : { command: 'npx', args: ['--yes', '@jam-mcp/launcher'] };
2061
+ const adapters = [
2062
+ new GitHubAdapter(),
2063
+ new GitLabAdapter(),
2064
+ new JamAdapter(jamCommand ?? {}),
2065
+ ];
2066
+ const declared = resolved?.layers.profile.bindings ?? [];
2067
+ const plan = await composeBindings({
2068
+ context: { projectRoot, env: process.env },
2069
+ adapters,
2070
+ roles: declared.map((b) => ({ adapterId: b.adapter, resource: b.resource, role: b.role })),
2071
+ });
2072
+ // ์„ ์–ธ์ด ์—†์œผ๋ฉด ๋ฐœ๊ฒฌ๋œ ์‚ฌ์‹ค๋กœ ์ œ์•ˆํ•œ๋‹ค (P1-G). ์ €์žฅํ•˜์ง€ ์•Š๊ณ , ๊ฐˆ๋ฆฌ๋ฉด ๊ณ ๋ฅด์ง€ ์•Š๋Š”๋‹ค.
2073
+ const proposed = declared.length === 0 ? proposeBindings(plan) : undefined;
2074
+ if (proposed) {
2075
+ for (const reason of proposed.reasons)
2076
+ console.error(` ์ œ์•ˆ ${reason}`);
2077
+ for (const conflict of proposed.conflicts)
2078
+ console.error(` ๋ณด๋ฅ˜ ${conflict}`);
2079
+ }
2080
+ const ports = await buildRuntimePorts({
2081
+ plan,
2082
+ // ์ œ์•ˆ์€ **๋งํ•˜๋Š” ๊ฒƒ**์ด์ง€ ์ •ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ์—ญํ• ์„ ๋ฐ•์•„ ๋„ฃ์œผ๋ฉด ์„ ์–ธ๊ณผ ๊ตฌ๋ถ„๋˜์ง€ ์•Š๊ณ ,
2083
+ // capability ํ•ด์„์€ ํ›„๋ณด๊ฐ€ ์œ ์ผํ•  ๋•Œ ์ด๋ฏธ ์Šค์Šค๋กœ ํ’€๋ฆฐ๋‹ค.
2084
+ roles: rolesFor(plan, declared),
2085
+ perPage: 30,
2086
+ jam: { command: jamCommand?.command ?? 'jam', args: [...(jamCommand?.args ?? []), 'serve'], cwd: projectRoot },
2087
+ endpointFor: (binding) => endpointOf(adapters, binding),
2088
+ });
2089
+ const work = ports.resourceContext;
2090
+ if (!work)
2091
+ return undefined;
2092
+ // ์ €์žฅ์†Œ๋Š” ์›๊ฒฉ provider ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋ณธ๋‹ค. ์ด ํ•œ ์ค„์ด P0-E ์˜ ์š”์ ์ด๋‹ค.
2093
+ const repo = new LocalRepoAdapter({ cwd: projectRoot });
2094
+ const canonicalSource = resolved?.layers.profile.canonical.sources[0];
2095
+ const canonicalRef = canonicalSource?.ref;
2096
+ // remote ๋ฅผ ๋ฒ„๋ฆฌ๋ฉด ๋กœ์ปฌ ๋ธŒ๋žœ์น˜๋ฅผ ์ •๋ณธ์ฒ˜๋Ÿผ ์ฝ๋Š”๋‹ค โ€” ์‹ค์ „ ์˜คํŒ์˜ ๊ฒฝ๋กœ์˜€๋‹ค.
2097
+ const canonicalRemote = canonicalSource?.remote;
2098
+ const canonicalPaths = resolved?.layers.profile.canonical.sources.flatMap((source) => source.paths) ?? [];
2099
+ const changeContext = ports.changeContext;
2100
+ return {
2101
+ gather: async (workRef) => {
2102
+ const workItem = await work.getResource(workRef).catch(() => undefined);
2103
+ const comments = await work
2104
+ .getComments(workRef, { limit: 20 })
2105
+ .then((list) => list)
2106
+ .catch(() => 'UNAVAILABLE');
2107
+ const change = changeContext
2108
+ ? await changeContext
2109
+ .getChange(workRef)
2110
+ .then((summary) => summary)
2111
+ .catch(() => 'UNAVAILABLE')
2112
+ : 'UNAVAILABLE';
2113
+ // ์„ ํ–‰ ์ž‘์—…์ด ์—ด๋ ค ์žˆ๋Š”์ง€๋Š” **์กฐํšŒํ•ด์•ผ** ์•ˆ๋‹ค. ํ‚ค๋งŒ ๋„˜๊ธฐ๋ฉด ํŒ์ •์ด ๋Š˜ "๋ชจ๋ฅธ๋‹ค"๊ฐ€ ๋˜๊ณ ,
2114
+ // ๊ทธ๋Ÿฌ๋ฉด ๋ง‰ํžŒ ์ž‘์—…์ด ์ฐฉ์ˆ˜ ๊ฐ€๋Šฅ์œผ๋กœ ๋ณด์ธ๋‹ค. adapter ๊ฐ€ ๋ง‰๋Š” ๊ฒƒ์„ ์•ž์— ์‹ค์–ด ์ฃผ๋ฏ€๋กœ
2115
+ // ์ƒํ•œ์— ๊ฑธ๋ ค๋„ blocker ๊ฐ€ ๋จผ์ € ํ™•์ธ๋œ๋‹ค.
2116
+ const candidates = dependencyCandidates(workItem);
2117
+ const dependencies = await Promise.all(candidates.map(async (reference) => {
2118
+ const item = await work.getResource(reference).catch(() => undefined);
2119
+ const done = item && !item.missing ? statusIndicatesDone(item.state) : undefined;
2120
+ return {
2121
+ reference,
2122
+ ...(item?.state ? { state: item.state } : {}),
2123
+ ...(done === undefined ? {} : { open: !done }),
2124
+ };
2125
+ }));
2126
+ return {
2127
+ ...(workItem ? { workItem } : {}),
2128
+ ...(workItem ? { trackerDone: statusIndicatesDone(workItem.state) } : {}),
2129
+ comments,
2130
+ change,
2131
+ dependencies,
2132
+ };
2133
+ },
2134
+ observeRepo: async (query) => {
2135
+ // ์กฐํšŒํ•  ๊ฒฝ๋กœ๋Š” **์ž‘์—… ํ•ญ๋ชฉ์ด ์ง€๋ชฉํ•œ ๊ฒƒ**์ด ๋จผ์ €๋‹ค. ๊ทธ๊ฒƒ์„ ํ™•์ธํ•ด์•ผ ์ข์€ ๋ฒ”์œ„๋ฅผ
2136
+ // ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ณ , ํ™•์ธํ•˜์ง€ ์•Š์œผ๋ฉด ๋„“์€ ๋ฒ”์œ„๋ฐ–์— ๋‚จ์ง€ ์•Š๋Š”๋‹ค. ์ €์žฅ์†Œ์˜ ์ตœ์ƒ์œ„ ์ž๋ฆฌ๋„
2137
+ // ํ•จ๊ป˜ ํ™•์ธํ•œ๋‹ค โ€” ๋ถ„๋ฅ˜ ์ด๋ฆ„์ด ์–ด๋А ๋ชจ๋“ˆ ํ•˜๋‚˜์—๋งŒ ๋งž๋Š”์ง€ ์žฌ๋ ค๋ฉด ๊ทธ ๋ชฉ๋ก์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.
2138
+ const modules = await topLevelModules(projectRoot);
2139
+ const paths = [...new Set([...(query.paths ?? []), ...canonicalPaths])];
2140
+ return repo.observe({
2141
+ refHint: query.refHint,
2142
+ ...(canonicalRef ? { canonicalRef } : {}),
2143
+ ...(canonicalRemote ? { remote: canonicalRemote } : {}),
2144
+ ...(paths.length > 0 ? { paths } : {}),
2145
+ ...(modules.length > 0 ? { modulePaths: modules } : {}),
2146
+ });
2147
+ },
2148
+ usedIds: () => usedSessionIds(store, root),
2149
+ derive: (input) => deriveSessionContractDraft({
2150
+ existingIds: input.existingIds,
2151
+ intent: { workRef: input.workRef, ...(input.goal ? { goal: input.goal } : {}) },
2152
+ workItem: input.workItem,
2153
+ workState: input.workState,
2154
+ repo: input.repo,
2155
+ // ์ƒํ•œ์ด์ง€ ์ถœ์ฒ˜๊ฐ€ ์•„๋‹ˆ๋‹ค โ€” ๋„์ถœํ•œ ํ›„๋ณด๊ฐ€ ์ด ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€์ง€ ์•Š๋Š”์ง€ ์žฌ๋Š” ๋ฐ๋งŒ ์“ด๋‹ค.
2156
+ maxScopes: resolved?.resolved.policy.roleScopes.implementer ?? [],
2157
+ ...(resolved?.ownership ? { ownership: resolved.ownership } : {}),
2158
+ today: new Date().toISOString().slice(0, 10).replace(/-/g, ''),
2159
+ }),
2160
+ plan: async (draft) => planSessionContract({
2161
+ draft,
2162
+ ...(resolved?.resolved.policy ? { policy: resolved.resolved.policy } : {}),
2163
+ ...(resolved?.ownership ? { ownership: resolved.ownership } : {}),
2164
+ existingIds: await usedSessionIds(store, root),
2165
+ }),
2166
+ issue: async (draft) => {
2167
+ // ๋ฐœ๊ธ‰ ๊ฒฝ๋กœ๋Š” ํ•˜๋‚˜๋ฟ์ด๋‹ค โ€” `asc session issue` ์™€ ๊ฐ™์€ SessionRuntime.issue ๋ฅผ ๋ถ€๋ฅธ๋‹ค.
2168
+ const issued = await runtime.issue({
2169
+ id: draft.id,
2170
+ role: SessionRole.parse(draft.role ?? 'implementer'),
2171
+ goal: draft.goal ?? '',
2172
+ ...(draft.criteria ? { doneCriteria: [...draft.criteria] } : {}),
2173
+ ...(draft.boundary ? { writeBoundary: [...draft.boundary] } : {}),
2174
+ ...(draft.owner ? { owner: draft.owner } : {}),
2175
+ });
2176
+ if (!issued.ok) {
2177
+ return { ok: false, detail: issued.failures.map((f) => `${f.kind}: ${f.detail}`).join('; ') };
2178
+ }
2179
+ await auditLedger(store).delegate({
2180
+ childSessionId: issued.session.id,
2181
+ role: issued.session.role,
2182
+ goal: issued.session.goal,
2183
+ scope: issued.session.writeBoundary,
2184
+ doneCriteria: issued.session.doneCriteria,
2185
+ issuedBy: issued.session.owner ?? '(์œ„์ž„ ๋ฒ”์œ„ ๋‚ด ์ž๋™ ๋ฐœ๊ธ‰)',
2186
+ issuedAt: new Date().toISOString(),
2187
+ });
2188
+ return { ok: true, sessionId: issued.session.id };
2189
+ },
2190
+ };
2191
+ }
1809
2192
  /** ์„ธ์…˜ lifecycle. ๊ณ„์•ฝ์„ ๋ฐœ๊ธ‰ํ•˜๊ณ , ์ค‘๋‹จยท์žฌ๊ฐœํ•˜๊ณ , Handoff๊นŒ์ง€ ๋‚จ๊ธด๋‹ค (OM ยง6.2). */
1810
2193
  /**
1811
2194
  * ์ด ์„ธ์…˜์ด ๊ฑธ๋ฆฐ ๊ฒฐ์ •๋งˆ๋‹ค ์‹ค์ œ ๊ฒฐ์ •๊ถŒ์ž. ์„ธ์…˜ ๊ณ„์•ฝ์ด ๋จผ์ €๊ณ , ์—†์œผ๋ฉด Profile ์ง€๋„์—์„œ ํ‘ผ๋‹ค.
@@ -1840,6 +2223,94 @@ function parseAuthority(pairs) {
1840
2223
  }
1841
2224
  return { ok: true, map };
1842
2225
  }
2226
+ /**
2227
+ * ์ดˆ์•ˆ์„ ์žฌ ๋ณธ๋‹ค. **์•„๋ฌด๊ฒƒ๋„ ๋ฐœ๊ธ‰ํ•˜์ง€ ์•Š๋Š”๋‹ค** (C-14 ยง6์˜ plan/apply ๋ถ„๋ฆฌ์™€ ๊ฐ™์€ ์ž์„ธ).
2228
+ *
2229
+ * agent๊ฐ€ ์‚ฌ์šฉ์ž ์š”์ฒญยทwork itemยทProfileยท์ €์žฅ์†Œ๋ฅผ ์ฝ์–ด ๊ณ„์•ฝ ์ดˆ์•ˆ์„ ๋งŒ๋“ค๊ณ , ์—ฌ๊ธฐ์„œ ๊ทธ๊ฒƒ์ด
2230
+ * ๊ตฌ์กฐยท๊ฒฝ๊ณ„๋กœ ์„ฑ๋ฆฝํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. ํŒ์ •์€ ์…‹๋ฟ์ด๋‹ค โ€” ๋ฐœ๊ธ‰ํ•ด๋„ ๋œ๋‹ค / ์‚ฌ๋žŒ์ด ์ •ํ•  ๊ฒƒ์ด
2231
+ * ๋‚จ์•˜๋‹ค / ์ด ์ดˆ์•ˆ์œผ๋กœ๋Š” ๊ณ„์•ฝ์ด ์•ˆ ๋œ๋‹ค.
2232
+ */
2233
+ async function runSessionPlan(values, store, resolved) {
2234
+ const authority = parseAuthority(values.authority);
2235
+ if (!authority.ok) {
2236
+ console.error(authority.detail);
2237
+ return 2;
2238
+ }
2239
+ // ์ถœ์ฒ˜๋Š” `<field>=<status>[:<source>]` ๋กœ ๋ฐ›๋Š”๋‹ค โ€” ์ดˆ์•ˆ์„ ๋งŒ๋“  ์ชฝ์ด ๋ฌด์—‡์„ ํ™•์ธํ–ˆ๊ณ 
2240
+ // ๋ฌด์—‡์„ ์ œ์•ˆํ–ˆ๋Š”์ง€ ์Šค์Šค๋กœ ์ ๊ฒŒ ํ•œ๋‹ค. ์ ์ง€ ์•Š์œผ๋ฉด ์ œ์•ˆ์œผ๋กœ ์…ˆํ•œ๋‹ค(์‚ฌ์‹ค๋กœ ์˜ฌ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค).
2241
+ const provenance = [];
2242
+ for (const raw of values.provenance ?? []) {
2243
+ const [field, rest] = raw.split('=', 2);
2244
+ const [status, source] = (rest ?? '').split(':', 2);
2245
+ const parsed = DraftProvenance.safeParse({
2246
+ field,
2247
+ status,
2248
+ source: source ?? 'agent_proposal',
2249
+ ...(values.why ? { reason: values.why[0] } : {}),
2250
+ });
2251
+ if (!parsed.success) {
2252
+ console.error(`--provenance ๋Š” <field>=<FACT|PROPOSAL|DECISION_REQUIRED>[:<source>] ํ˜•์‹์ด๋‹ค: '${raw}'`);
2253
+ return 2;
2254
+ }
2255
+ provenance.push(parsed.data);
2256
+ }
2257
+ const draft = {
2258
+ ...(values.id ? { id: values.id } : {}),
2259
+ ...(values.role ? { role: values.role } : {}),
2260
+ ...(values.goal ? { goal: values.goal } : {}),
2261
+ ...(values.boundary ? { boundary: values.boundary } : {}),
2262
+ ...(values.criteria ? { criteria: values.criteria } : {}),
2263
+ ...(values.owner ? { owner: values.owner } : {}),
2264
+ ...(values.domain ? { decisionDomains: values.domain } : {}),
2265
+ ...(Object.keys(authority.map).length > 0 ? { decisionAuthority: authority.map } : {}),
2266
+ ...(provenance.length > 0 ? { provenance } : {}),
2267
+ };
2268
+ const plan = planSessionContract({
2269
+ draft,
2270
+ ...(resolved?.resolved.policy ? { policy: resolved.resolved.policy } : {}),
2271
+ ...(resolved?.ownership ? { ownership: resolved.ownership } : {}),
2272
+ existingIds: (await store.list('session')).map((session) => session.id),
2273
+ });
2274
+ // ํ†ต๊ณผํ–ˆ์„ ๋•Œ๋งŒ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๋ช…๋ น์„ ์ค€๋‹ค. ํ†ต๊ณผํ•˜์ง€ ์•Š์€ ์ดˆ์•ˆ์˜ ๋ช…๋ น์„ ํ•จ๊ป˜ ์ฃผ๋ฉด
2275
+ // agent๋Š” ๊ทธ๊ฒƒ์„ "๊ณ ์น˜๊ณ  ๋‚˜์„œ ์“ธ ๊ฒƒ"์ด ์•„๋‹ˆ๋ผ "์ง€๊ธˆ ์“ธ ๊ฒƒ"์œผ๋กœ ์ฝ๋Š”๋‹ค.
2276
+ //
2277
+ // ๊ทธ๋ฆฌ๊ณ  **์™„์„ฑ๋œ ๊ณ„์•ฝ์ด๋ผ๊ณ  ํ•ด์„œ ๋ฐœ๊ธ‰ํ•ด๋„ ๋˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค** (OM ยง450). ์œ„์ž„์ด ์—†์œผ๋ฉด
2278
+ // ๋ช…๋ น์€ `forController` ๋กœ ๊ฐ„๋‹ค โ€” actions์— ๋„ฃ์œผ๋ฉด "portable์„ ์‹คํ–‰ํ•˜๋ผ"๋Š” ์ง€์‹œ๋ฅผ ๋”ฐ๋ฅด๋Š”
2279
+ // agent๊ฐ€ ์‚ฌ๋žŒ์˜ ๊ถŒํ•œ์„ ๋Œ€์‹  ์“ฐ๊ฒŒ ๋œ๋‹ค.
2280
+ const issuable = plan.status === 'READY_TO_ISSUE';
2281
+ const command = {
2282
+ display: shorthandCommand(issueArgs(draft)),
2283
+ portable: shorthandCommand([...issueArgs(draft), '--json']),
2284
+ };
2285
+ const actions = issuable && plan.issuance.authority === 'delegated' ? [{ type: 'issue_session', ...command }] : [];
2286
+ const forController = issuable && plan.issuance.authority === 'controller' ? command : undefined;
2287
+ if (values.json || values.agent) {
2288
+ console.log(JSON.stringify({ ...plan, nextActions: actions.map((action) => action.portable), actions, ...(forController ? { forController } : {}) }, null, 2));
2289
+ }
2290
+ else {
2291
+ console.log(`${plan.status}${plan.draft.id ? ` โ€” ${plan.draft.id}` : ''}`);
2292
+ for (const fact of plan.facts)
2293
+ console.log(` fact ${fact.field} (${fact.source})`);
2294
+ for (const proposal of plan.proposals)
2295
+ console.log(` proposal ${proposal.field} โ€” ${proposal.reason ?? proposal.source}`);
2296
+ for (const item of plan.invalid)
2297
+ console.log(` invalid ${item.field}: ${item.detail}`);
2298
+ for (const item of plan.unresolved) {
2299
+ console.log(` decide ${item.field} [${item.reason}]: ${item.detail}`);
2300
+ for (const [index, option] of (item.options ?? []).entries()) {
2301
+ console.log(` ${index + 1}. ${option}${item.recommended === index ? ' โ† recommended' : ''}`);
2302
+ }
2303
+ }
2304
+ for (const action of actions)
2305
+ console.log(`\nIssue it: ${action.display}`);
2306
+ if (forController) {
2307
+ console.log(`\nThe contract holds. Issuing it is the Controller's โ€” ${plan.issuance.detail}:`);
2308
+ console.log(` ${forController.display}`);
2309
+ }
2310
+ }
2311
+ // ์‚ฌ๋žŒ์ด ์ •ํ•  ๊ฒƒ์ด ๋‚จ์•˜๊ฑฐ๋‚˜ ์ดˆ์•ˆ์ด ์„ฑ๋ฆฝํ•˜์ง€ ์•Š์œผ๋ฉด 1์ด๋‹ค โ€” setup plan๊ณผ ๊ฐ™์€ ๊ทœ์น™.
2312
+ return plan.status === 'READY_TO_ISSUE' ? 0 : 1;
2313
+ }
1843
2314
  async function runSession(command, target, values, store, resolved) {
1844
2315
  // Profile์ด ์ •ํ•œ Role ๋ฒ”์œ„์™€ ๊ธˆ์ง€๊ฐ€ ์—ฌ๊ธฐ๊นŒ์ง€ ์™€์•ผ ์‹ค์ œ๋กœ ๊ฐ•์ œ๋œ๋‹ค.
1845
2316
  // ์ •์ฑ… ์—†์ด ๋งŒ๋“ค๋ฉด ๋ถ™์–ด ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์—์„œ๋„ ๊ณ„์•ฝ ๊ฒ€์‚ฌ๊ฐ€ ํ†ต์งธ๋กœ ๋น„์–ด๋ฒ„๋ฆฐ๋‹ค.
@@ -1863,6 +2334,10 @@ async function runSession(command, target, values, store, resolved) {
1863
2334
  }
1864
2335
  return 0;
1865
2336
  }
2337
+ // `plan` ์€ **์•„์ง ์„ธ์…˜์ด ์—†์„ ๋•Œ** ๋ถ€๋ฅด๋Š” ๊ฒƒ์ด๋ฏ€๋กœ id๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ดˆ์•ˆ์— id๊ฐ€
2338
+ // ์—†๋‹ค๋Š” ์‚ฌ์‹ค ์ž์ฒด๊ฐ€ ํŒ์ • ๋Œ€์ƒ์ด๋‹ค (์—†์œผ๋ฉด ๊ทธ๊ฒƒ์ด unresolved๋กœ ๋‚˜์˜จ๋‹ค).
2339
+ if (command === 'plan')
2340
+ return runSessionPlan(values, store, resolved);
1866
2341
  if (!target) {
1867
2342
  console.error(`Usage: asc session ${command ?? '<command>'} <SESSION_ID>`);
1868
2343
  return 2;